From 553796d548476a3279019c9f96aceea59c8aad8a Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 27 Jun 2023 00:14:25 -0400 Subject: [PATCH 01/20] feat: add terminal task tracker --- legacy/packages/core/src/agentic.ts | 8 + legacy/packages/core/src/constants.ts | 1 + legacy/packages/core/src/logger.ts | 2 +- legacy/packages/core/src/task.ts | 20 ++ legacy/src/events/event.ts | 166 ++++++++++++++++ legacy/src/events/index.ts | 2 + legacy/src/events/symbols.ts | 32 ++++ legacy/src/events/tracker.ts | 262 ++++++++++++++++++++++++++ 8 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 legacy/src/events/event.ts create mode 100644 legacy/src/events/index.ts create mode 100644 legacy/src/events/symbols.ts create mode 100644 legacy/src/events/tracker.ts diff --git a/legacy/packages/core/src/agentic.ts b/legacy/packages/core/src/agentic.ts index 5f242961..205f73d6 100644 --- a/legacy/packages/core/src/agentic.ts +++ b/legacy/packages/core/src/agentic.ts @@ -3,6 +3,7 @@ import { SetOptional } from 'type-fest' import * as types from './types' import { DEFAULT_OPENAI_MODEL } from './constants' +import { TerminalTaskTracker, defaultTaskTracker } from './events' import { HumanFeedbackOptions, HumanFeedbackType } from './human-feedback' import { HumanFeedbackMechanismCLI } from './human-feedback/cli' import { OpenAIChatCompletion } from './llms/openai' @@ -12,6 +13,7 @@ import { defaultIDGeneratorFn, isFunction, isString } from './utils' export class Agentic { protected _ky: types.KyInstance protected _logger: types.Logger + protected _taskTracker: TerminalTaskTracker protected _openai?: types.openai.OpenAIClient protected _anthropic?: types.anthropic.Client @@ -35,6 +37,7 @@ export class Agentic { idGeneratorFn?: types.IDGeneratorFunction logger?: types.Logger ky?: types.KyInstance + taskTracker?: TerminalTaskTracker }) { // TODO: This is a bit hacky, but we're doing it to have a slightly nicer API // for the end developer when creating subclasses of `BaseTask` to use as @@ -48,6 +51,7 @@ export class Agentic { this._ky = opts.ky ?? defaultKy this._logger = opts.logger ?? defaultLogger + this._taskTracker = opts.taskTracker ?? defaultTaskTracker this._openaiModelDefaults = { provider: 'openai', @@ -98,6 +102,10 @@ export class Agentic { return this._humanFeedbackDefaults } + public get taskTracker(): TerminalTaskTracker { + return this._taskTracker + } + public get idGeneratorFn(): types.IDGeneratorFunction { return this._idGeneratorFn } diff --git a/legacy/packages/core/src/constants.ts b/legacy/packages/core/src/constants.ts index dc85269f..207c67de 100644 --- a/legacy/packages/core/src/constants.ts +++ b/legacy/packages/core/src/constants.ts @@ -2,3 +2,4 @@ export const DEFAULT_OPENAI_MODEL = 'gpt-3.5-turbo' export const DEFAULT_ANTHROPIC_MODEL = 'claude-instant-v1' export const DEFAULT_BOT_NAME = 'Agentic Bot' export const SKIP_HOOKS = Symbol('SKIP_HOOKS') +export const SPACE = ' ' diff --git a/legacy/packages/core/src/logger.ts b/legacy/packages/core/src/logger.ts index 9611cc4a..546e171a 100644 --- a/legacy/packages/core/src/logger.ts +++ b/legacy/packages/core/src/logger.ts @@ -1,6 +1,7 @@ import { cyan, green, magenta, red, yellow } from 'colorette' import logger from 'debug' +import { SPACE } from '@/constants' import { identity } from '@/utils' import { getEnv } from './env' @@ -57,7 +58,6 @@ if (LOG_LEVEL === undefined) { const debug = logger('agentic') -const SPACE = ' ' const INDENT = SPACE.repeat(23) // Override the default logger to add a timestamp and severity level to the logged arguments: diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index a8eb3ee3..a01573df 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -6,6 +6,7 @@ import * as errors from './errors' import * as types from './types' import type { Agentic } from './agentic' import { SKIP_HOOKS } from './constants' +import { TaskEvent, TaskStatus } from './events' import { HumanFeedbackMechanismCLI, HumanFeedbackOptions, @@ -272,6 +273,15 @@ export abstract class BaseTask< } } + const taskEvent = new TaskEvent({ + payload: { + taskStatus: TaskStatus.RUNNING, + taskInputs: input, + ...ctx.metadata + } + }) + this._agentic.taskTracker.addEvent(taskEvent) + for (const { hook: preHook } of this._preHooks) { const preHookResult = await preHook(ctx) if (preHookResult === SKIP_HOOKS) { @@ -381,6 +391,16 @@ export abstract class BaseTask< // ctx.tracker.setOutput(stringifyForDebugging(result, { maxLength: 100 })) + const taskEvent2 = new TaskEvent({ + payload: { + taskInputs: input, + taskStatus: TaskStatus.SUCCEEDED, + taskOutput: result, + ...ctx.metadata + } + }) + this._agentic.taskTracker.addEvent(taskEvent2) + return { result, metadata: ctx.metadata diff --git a/legacy/src/events/event.ts b/legacy/src/events/event.ts new file mode 100644 index 00000000..ddc1c8c3 --- /dev/null +++ b/legacy/src/events/event.ts @@ -0,0 +1,166 @@ +import { Jsonifiable } from '@/types' +import { defaultIDGeneratorFn } from '@/utils' + +/** + * Payload of an event. + */ +interface EventPayload { + [key: string]: unknown +} + +/** + * Data required to create a new Event object. + */ +interface EventData { + id?: string + timestamp?: Date + payload?: EventPayload + version?: number +} + +/** + * Events that occur within the library (should be treated as immutable). + */ +export class Event { + public readonly id: string + public readonly timestamp: Date + public readonly payload?: EventPayload + public readonly version: number + + constructor(data: EventData = {}) { + this.id = defaultIDGeneratorFn() + this.timestamp = data.timestamp ?? new Date() + + this.payload = data.payload + ? JSON.parse(JSON.stringify(data.payload)) + : undefined + this.version = data.version ?? 1 // Default to version 1 if not provided... + } + + /** + * Converts a JSON string representation of an event back into an Event object. + */ + static fromJSON(json: string): Event { + const data = JSON.parse(json) + + // Convert the timestamp back into a Date object, since `JSON.parse()` will have turned it into a string: + data.timestamp = new Date(data.timestamp) + const event = new Event(data) + return event + } + + /** + * Converts the event to a JSON string representation. + * + * @returns JSON representation + */ + toJSON(): string { + return JSON.stringify({ + id: this.id, + timestamp: this.timestamp.toISOString(), + payload: this.payload, + version: this.version + }) + } + + /** + * Converts the event to a human-readable string representation suitable for logging. + * + * @returns string representation + */ + toString(): string { + return `Event { id: ${ + this.id + }, timestamp: ${this.timestamp.toISOString()}, payload: ${JSON.stringify( + this.payload + )} }` + } +} + +/** + * Payload of a task event. + */ +interface TaskEventPayload extends EventPayload { + taskName: string + taskId: string + taskStatus: TaskStatus + taskInputs: any // Consider replacing 'any' with the actual task data type if possible., + taskOutput?: any // Consider replacing 'any' with the actual task data type if possible., + taskParent?: string +} + +/** + * Data required to create a new TaskEvent object. + */ +interface TaskEventData extends EventData { + payload?: TaskEventPayload +} + +/** + * Status of a task. + */ +export enum TaskStatus { + SUCCEEDED = 'SUCCEEDED', + FAILED = 'FAILED', + RETRYING = 'RETRYING', + SKIPPED = 'SKIPPED', + RUNNING = 'RUNNING', + CANCELLED = 'CANCELLED' +} + +/** + * Events that occur within the library related to tasks. + */ +export class TaskEvent extends Event { + public readonly name: string + public readonly taskId: string + public readonly status: TaskStatus + public readonly inputs: any + public readonly output?: Jsonifiable + public readonly parent?: string + + constructor(data: TaskEventData = {}) { + super(data) + + this.name = data.payload?.taskName ?? '' + this.taskId = data.payload?.taskId ?? '' + this.status = data.payload?.taskStatus ?? TaskStatus.RUNNING + this.inputs = data.payload?.taskData ?? '' + this.output = data.payload?.taskOutput ?? '' + this.parent = data.payload?.taskParent ?? 'root' + } + + /** + * Converts a JSON string representation of a task event back into a TaskEvent object. + */ + static fromJSON(json: string): TaskEvent { + const data = JSON.parse(json) + // Convert the timestamp back into a Date object, since `JSON.parse()` will have turned it into a string: + data.timestamp = new Date(data.timestamp) + const taskEvent = new TaskEvent(data) + return taskEvent + } + + /** + * Converts the task event to a JSON string representation. + */ + toJSON(): string { + return JSON.stringify({ + id: this.id, + timestamp: this.timestamp.toISOString(), + payload: this.payload, + version: this.version + }) + } + + /** + * Converts the task event to a human-readable string representation suitable for logging. + */ + toString(): string { + return `TaskEvent { id: ${ + this.id + }, timestamp: ${this.timestamp.toISOString()}, payload: ${JSON.stringify( + this.payload + )} }` + } +} diff --git a/legacy/src/events/index.ts b/legacy/src/events/index.ts new file mode 100644 index 00000000..b15f5f24 --- /dev/null +++ b/legacy/src/events/index.ts @@ -0,0 +1,2 @@ +export * from './event' +export * from './tracker' diff --git a/legacy/src/events/symbols.ts b/legacy/src/events/symbols.ts new file mode 100644 index 00000000..02fbda76 --- /dev/null +++ b/legacy/src/events/symbols.ts @@ -0,0 +1,32 @@ +import isUnicodeSupported from 'is-unicode-supported' + +const UNICODE_SYMBOLS = { + ARROW_RIGHT: '→', + CIRCLE: '●', + WARNING: '▲', + CROSS: '⨯', + SQUARE_SMALL_FILLED: '◼', + SPINNER: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'], + BAR_START: '┌', + BAR: '│', + BAR_END: '└', + ACTIVE: '◆', + LEFT_ARROW: '←', + RIGHT_ARROW: '→' +} +const ASCII_SYMBOLS = { + ARROW_RIGHT: '→', + CIRCLE: '•', + WARNING: '‼', + CROSS: '×', + SQUARE_SMALL_FILLED: '■', + SPINNER: ['-', '\\', '|', '/'], + BAR_START: 'T', + BAR: '|', + BAR_END: '—', + ACTIVE: '*', + LEFT_ARROW: '<', + RIGHT_ARROW: '>' +} + +export const SYMBOLS = isUnicodeSupported() ? UNICODE_SYMBOLS : ASCII_SYMBOLS diff --git a/legacy/src/events/tracker.ts b/legacy/src/events/tracker.ts new file mode 100644 index 00000000..b3023554 --- /dev/null +++ b/legacy/src/events/tracker.ts @@ -0,0 +1,262 @@ +import process from 'node:process' +import readline from 'node:readline' + +import { bold, cyan, gray, green, red, yellow } from 'colorette' + +import { SPACE } from '@/constants' + +import { TaskEvent, TaskStatus } from './event' +import { SYMBOLS } from './symbols' + +const consoleBuffer: any[] = [] + +const originalWrite = process.stdout.write +process.stdout.write = function (str: any) { + consoleBuffer.push(str) + return originalWrite.call(process.stdout, str) +} + +const SPINNER_INTERVAL = 100 +const INACTIVITY_THRESHOLD = 2000 // 2 seconds + +function getSpinnerSymbol() { + return SYMBOLS.SPINNER[ + Math.floor(Date.now() / SPINNER_INTERVAL) % SYMBOLS.SPINNER.length + ] +} + +export class TerminalTaskTracker { + private events: Record = { root: [] } + private interval: NodeJS.Timeout | null = null + private inactivityTimeout: NodeJS.Timeout | null = null + private truncateOutput = false + private renderTasks = true + private outputs: Array = [] + + constructor() { + if (!process.stderr.isTTY) { + // If stderr is not a TTY, don't render any dynamic output... + return + } + + this.start() + } + + renderOutput() { + const output = this.outputs.join('') + process.stderr.write(output) + } + + handleKeyPress = (str, key) => { + if (key.ctrl && key.name === 'c') { + process.exit() + } + + if (key.ctrl && key.name === 'e') { + this.toggleOutputTruncation() + } + + if (key.ctrl && key.name === 'o') { + this.renderTasks = !this.renderTasks + } + } + + start() { + this.interval = setInterval(() => { + this.render() + }, SPINNER_INTERVAL) + + readline.emitKeypressEvents(process.stdin) + + process.stdin.setRawMode(true) + + process.stdin.on('keypress', this.handleKeyPress) + + this.startInactivityTimeout() + } + + close() { + if (this.interval) { + clearInterval(this.interval) + } + + if (this.inactivityTimeout) { + clearTimeout(this.inactivityTimeout) + } + + process.stdin.setRawMode(false) + + // Remove the keypress listener: + process.stdin.off('keypress', this.handleKeyPress) + + process.stderr.write('\n') + process.stderr.write('\n') + process.stderr.write('Completed all tasks.\n') + process.stderr.write('\n') + process.stderr.write('stdout:\n') + process.stderr.write('\n') + process.stderr.write(consoleBuffer.join('')) + + // Restore the original `process.stdout.write()` function: + process.stdout.write = originalWrite + + // Pause the reading of stdin so that the Node.js process will exit once done: + process.stdin.pause() + } + + stringify(value: any) { + if (this.truncateOutput) { + const json = JSON.stringify(value) + if (json.length < 40) { + return json + } + + return json.slice(0, 20) + '...' + json.slice(-20) + } + + return JSON.stringify(value) + } + + toggleOutputTruncation() { + this.truncateOutput = !this.truncateOutput + } + + startInactivityTimeout() { + this.inactivityTimeout = setTimeout(() => { + // Check if all tasks are completed: + const allTasksCompleted = Object.values(this.events).every((events) => + events.every((event) => event.status !== TaskStatus.RUNNING) + ) + + if (allTasksCompleted) { + this.close() + } else { + this.startInactivityTimeout() + } + }, INACTIVITY_THRESHOLD) + } + + addEvent(event: TaskEvent) { + const { parent = 'root', taskId, name, status, inputs, output } = event + if (!this.events[parent]) { + this.events[parent] = [] + } + + const existingEventIndex = this.events[parent].findIndex( + (e) => e.taskId === taskId + ) + + if (existingEventIndex !== -1) { + // If the event already exists, update its status and output: + this.events[parent][existingEventIndex].status = status + this.events[parent][existingEventIndex].output = output + } else { + // If the event does not exist, add it to the array: + this.events[parent].push({ taskId, name, status, inputs }) + } + } + + private getStatusSymbolColor( + status: TaskStatus + ): [string, (text: string) => string] { + switch (status) { + case TaskStatus.SUCCEEDED: + return [SYMBOLS.CIRCLE, green] + case TaskStatus.FAILED: + return [SYMBOLS.CROSS, red] + case TaskStatus.RETRYING: + return [SYMBOLS.WARNING, yellow] + case TaskStatus.RUNNING: + default: + return [getSpinnerSymbol(), cyan] + } + } + + renderTree(node: string, level = 0) { + const indent = SPACE.repeat(level * 2) + let lines: string[] = [] + + if (this.events[node]) { + this.events[node].forEach(({ name, status, output, inputs }) => { + const [statusSymbol, color] = this.getStatusSymbolColor(status) + + lines.push( + indent + + color(statusSymbol) + + SPACE + + bold(name) + + gray('(' + this.stringify(inputs) + ')') + ) + + if (this.events[name]) { + lines = lines.concat( + this.renderTree(name, level + 1).map((line, index, arr) => { + if (index === arr.length - 1) { + return indent + gray(SYMBOLS.BAR) + line + } + + return indent + gray(SYMBOLS.BAR) + line + }) + ) + } + + let line = '' + if (this.events[name]) { + line = indent + gray(SYMBOLS.BAR_END) + } + + if (output) { + if (status === TaskStatus.SUCCEEDED) { + const formattedOutput = this.stringify(output) + line += + indent + + ' ' + + gray(SYMBOLS.RIGHT_ARROW + SPACE + formattedOutput) + } else if (status === TaskStatus.FAILED) { + line += + indent + ' ' + gray(SYMBOLS.RIGHT_ARROW + SPACE + red(output)) + } else if (status === TaskStatus.RETRYING) { + line += + indent + ' ' + gray(SYMBOLS.RIGHT_ARROW + SPACE + yellow(output)) + } + } + + lines.push(line) + }) + } + + return lines + } + + clearAndSetCursorPosition() { + process.stderr.cursorTo(0, 0) + process.stderr.clearScreenDown() + } + + clearPreviousRender(linesCount: number) { + for (let i = 0; i < linesCount; i++) { + process.stderr.moveCursor(0, -1) + process.stderr.clearLine(1) + } + } + + writeToConsole(lines: string[]) { + if (lines.length > 0) { + process.stderr.write(lines.join('\n')) + } + } + + render() { + this.clearAndSetCursorPosition() + const lines = this.renderTree('root') + if (this.renderTasks) { + this.clearPreviousRender(lines.length + 1) + this.writeToConsole(lines) + } else { + this.clearPreviousRender(lines.length + 1) + this.writeToConsole(consoleBuffer) + } + } +} + +export const defaultTaskTracker = new TerminalTaskTracker() From 8971ff64775f7e77ed68853e1fb2f4c425cbc0be Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 27 Jun 2023 11:27:48 -0400 Subject: [PATCH 02/20] chore: add eventemitter3 and remove unused deps --- legacy/package.json | 38 ++++- legacy/pnpm-lock.yaml | 313 ++++++++++++++++++++++++++++-------------- 2 files changed, 248 insertions(+), 103 deletions(-) diff --git a/legacy/package.json b/legacy/package.json index 94115b60..1529f751 100644 --- a/legacy/package.json +++ b/legacy/package.json @@ -23,12 +23,46 @@ "release": "turbo clean && turbo build && changeset publish", "test": "turbo test", "pre-commit": "lint-staged", - "version-packages": "changeset version" + "test:prettier": "prettier \"**/*.{js,jsx,ts,tsx}\" --check", + "test:eslint": "eslint \"**/*.ts\"", + "test-cov": "c8 ava" + }, + "dependencies": { + "@agentic/midjourney-fetch": "^1.0.1", + "@anthropic-ai/sdk": "^0.4.4", + "@inquirer/checkbox": "^1.3.2", + "@inquirer/editor": "^1.2.1", + "@inquirer/input": "^1.2.2", + "@inquirer/select": "^1.2.2", + "colorette": "^2.0.20", + "debug": "^4.3.4", + "eventemitter3": "^5.0.1", + "expr-eval": "^2.0.2", + "handlebars": "^4.7.7", + "is-relative-url": "^4.0.0", + "is-unicode-supported": "^1.3.0", + "js-tiktoken": "^1.0.7", + "jsonrepair": "^3.2.0", + "ky": "^0.33.3", + "nanoid": "^4.0.2", + "normalize-url": "^8.0.0", + "openai-fetch": "^1.6.3", + "p-map": "^6.0.0", + "p-retry": "^5.1.2", + "p-throttle": "^5.1.0", + "p-timeout": "^6.1.2", + "quick-lru": "^6.1.1", + "replicate": "^0.12.3", + "ts-dedent": "^2.2.0", + "zod": "^3.21.4", + "zod-to-json-schema": "^3.21.2", + "zod-validation-error": "^1.3.1" }, "devDependencies": { "@changesets/cli": "^2.26.2", "@trivago/prettier-plugin-sort-imports": "^4.1.1", "@types/node": "^20.3.2", + "@types/sinon": "^10.0.15", "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "ava": "^5.3.1", @@ -71,4 +105,4 @@ "guardrails", "plugins" ] -} +} \ No newline at end of file diff --git a/legacy/pnpm-lock.yaml b/legacy/pnpm-lock.yaml index 834c2c49..8aec885e 100644 --- a/legacy/pnpm-lock.yaml +++ b/legacy/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -7,6 +7,94 @@ settings: importers: .: + dependencies: + '@agentic/midjourney-fetch': + specifier: ^1.0.1 + version: 1.0.1 + '@anthropic-ai/sdk': + specifier: ^0.4.4 + version: 0.4.4 + '@inquirer/checkbox': + specifier: ^1.3.2 + version: 1.3.2 + '@inquirer/editor': + specifier: ^1.2.1 + version: 1.2.1 + '@inquirer/input': + specifier: ^1.2.2 + version: 1.2.2 + '@inquirer/select': + specifier: ^1.2.2 + version: 1.2.2 + colorette: + specifier: ^2.0.20 + version: 2.0.20 + debug: + specifier: ^4.3.4 + version: 4.3.4 + eventemitter3: + specifier: ^5.0.1 + version: 5.0.1 + expr-eval: + specifier: ^2.0.2 + version: 2.0.2 + handlebars: + specifier: ^4.7.7 + version: 4.7.7 + is-relative-url: + specifier: ^4.0.0 + version: 4.0.0 + is-unicode-supported: + specifier: ^1.3.0 + version: 1.3.0 + js-tiktoken: + specifier: ^1.0.7 + version: 1.0.7 + jsonrepair: + specifier: ^3.2.0 + version: 3.2.0 + ky: + specifier: ^0.33.3 + version: 0.33.3 + nanoid: + specifier: ^4.0.2 + version: 4.0.2 + normalize-url: + specifier: ^8.0.0 + version: 8.0.0 + openai-fetch: + specifier: ^1.6.3 + version: 1.6.3 + p-map: + specifier: ^6.0.0 + version: 6.0.0 + p-retry: + specifier: ^5.1.2 + version: 5.1.2 + p-throttle: + specifier: ^5.1.0 + version: 5.1.0 + p-timeout: + specifier: ^6.1.2 + version: 6.1.2 + quick-lru: + specifier: ^6.1.1 + version: 6.1.1 + replicate: + specifier: ^0.12.3 + version: 0.12.3 + ts-dedent: + specifier: ^2.2.0 + version: 2.2.0 + zod: + specifier: ^3.21.4 + version: 3.21.4 + zod-to-json-schema: + specifier: ^3.21.2 + version: 3.21.2(zod@3.21.4) + zod-validation-error: + specifier: ^1.3.1 + version: 1.3.1(zod@3.21.4) devDependencies: '@changesets/cli': specifier: ^2.26.2 @@ -17,6 +105,9 @@ importers: '@types/node': specifier: ^20.3.2 version: 20.3.2 + '@types/sinon': + specifier: ^10.0.15 + version: 10.0.15 '@typescript-eslint/eslint-plugin': specifier: ^5.60.1 version: 5.60.1(@typescript-eslint/parser@5.60.1)(eslint@8.43.0)(typescript@5.1.6) @@ -216,11 +307,6 @@ importers: packages: - /@aashutoshrathi/word-wrap@1.2.6: - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - dev: true - /@agentic/midjourney-fetch@1.0.1: resolution: {integrity: sha512-CUGNh3YJfbAPrOKcmqOOWuWQrkgVY+pWbCGhZkq74Jlc6oP8Jb+/K61V7Jhf/liYgYopbrpoDDue+ZKqijW/2A==} engines: {node: '>=18', pnpm: '>=8'} @@ -550,7 +636,7 @@ packages: resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} dependencies: '@esbuild-kit/core-utils': 3.1.0 - get-tsconfig: 4.6.2 + get-tsconfig: 4.6.0 dev: true /@esbuild-kit/core-utils@3.1.0: @@ -564,7 +650,7 @@ packages: resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==} dependencies: '@esbuild-kit/core-utils': 3.1.0 - get-tsconfig: 4.6.2 + get-tsconfig: 4.6.0 dev: true /@esbuild/android-arm64@0.17.19: @@ -576,8 +662,8 @@ packages: dev: true optional: true - /@esbuild/android-arm64@0.18.10: - resolution: {integrity: sha512-ynm4naLbNbK0ajf9LUWtQB+6Vfg1Z/AplArqr4tGebC00Z6m9Y91OVIcjDa461wGcZwcaHYaZAab4yJxfhisTQ==} + /@esbuild/android-arm64@0.18.4: + resolution: {integrity: sha512-yQVgO+V307hA2XhzELQ6F91CBGX7gSnlVGAj5YIqjQOxThDpM7fOcHT2YLJbE6gNdPtgRSafQrsK8rJ9xHCaZg==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -594,8 +680,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.18.10: - resolution: {integrity: sha512-3KClmVNd+Fku82uZJz5C4Rx8m1PPmWUFz5Zkw8jkpZPOmsq+EG1TTOtw1OXkHuX3WczOFQigrtf60B1ijKwNsg==} + /@esbuild/android-arm@0.18.4: + resolution: {integrity: sha512-yKmQC9IiuvHdsNEbPHSprnMHg6OhL1cSeQZLzPpgzJBJ9ppEg9GAZN8MKj1TcmB4tZZUrq5xjK7KCmhwZP8iDA==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -612,8 +698,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.18.10: - resolution: {integrity: sha512-vFfXj8P9Yfjh54yqUDEHKzqzYuEfPyAOl3z7R9hjkwt+NCvbn9VMxX+IILnAfdImRBfYVItgSUsqGKhJFnBwZw==} + /@esbuild/android-x64@0.18.4: + resolution: {integrity: sha512-yLKXMxQg6sk1ntftxQ5uwyVgG4/S2E7UoOCc5N4YZW7fdkfRiYEXqm7CMuIfY2Vs3FTrNyKmSfNevIuIvJnMww==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -630,8 +716,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.18.10: - resolution: {integrity: sha512-k2OJQ7ZxE6sVc91+MQeZH9gFeDAH2uIYALPAwTjTCvcPy9Dzrf7V7gFUQPYkn09zloWhQ+nvxWHia2x2ZLR0sQ==} + /@esbuild/darwin-arm64@0.18.4: + resolution: {integrity: sha512-MVPEoZjZpk2xQ1zckZrb8eQuQib+QCzdmMs3YZAYEQPg+Rztk5pUxGyk8htZOC8Z38NMM29W+MqY9Sqo/sDGKw==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -648,8 +734,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.18.10: - resolution: {integrity: sha512-tnz/mdZk1L1Z3WpGjin/L2bKTe8/AKZpI8fcCLtH+gq8WXWsCNJSxlesAObV4qbtTl6pG5vmqFXfWUQ5hV8PAQ==} + /@esbuild/darwin-x64@0.18.4: + resolution: {integrity: sha512-uEsRtYRUDsz7i2tXg/t/SyF+5gU1cvi9B6B8i5ebJgtUUHJYWyIPIesmIOL4/+bywjxsDMA/XrNFMgMffLnh5A==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -666,8 +752,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.18.10: - resolution: {integrity: sha512-QJluV0LwBrbHnYYwSKC+K8RGz0g/EyhpQH1IxdoFT0nM7PfgjE+aS8wxq/KFEsU0JkL7U/EEKd3O8xVBxXb2aA==} + /@esbuild/freebsd-arm64@0.18.4: + resolution: {integrity: sha512-I8EOigqWnOHRin6Zp5Y1cfH3oT54bd7Sdz/VnpUNksbOtfp8IWRTH4pgkgO5jWaRQPjCpJcOpdRjYAMjPt8wXg==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -684,8 +770,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.18.10: - resolution: {integrity: sha512-Hi/ycUkS6KTw+U9G5PK5NoK7CZboicaKUSVs0FSiPNtuCTzK6HNM4DIgniH7hFaeuszDS9T4dhAHWiLSt/Y5Ng==} + /@esbuild/freebsd-x64@0.18.4: + resolution: {integrity: sha512-1bHfgMz/cNMjbpsYxjVgMJ1iwKq+NdDPlACBrWULD7ZdFmBQrhMicMaKb5CdmdVyvIwXmasOuF4r6Iq574kUTA==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -702,8 +788,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.18.10: - resolution: {integrity: sha512-Nz6XcfRBOO7jSrVpKAyEyFOPGhySPNlgumSDhWAspdQQ11ub/7/NZDMhWDFReE9QH/SsCOCLQbdj0atAk/HMOQ==} + /@esbuild/linux-arm64@0.18.4: + resolution: {integrity: sha512-J42vLHaYREyiBwH0eQE4/7H1DTfZx8FuxyWSictx4d7ezzuKE3XOkIvOg+SQzRz7T9HLVKzq2tvbAov4UfufBw==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -720,8 +806,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.18.10: - resolution: {integrity: sha512-HfFoxY172tVHPIvJy+FHxzB4l8xU7e5cxmNS11cQ2jt4JWAukn/7LXaPdZid41UyTweqa4P/1zs201gRGCTwHw==} + /@esbuild/linux-arm@0.18.4: + resolution: {integrity: sha512-4XCGqM/Ay1LCXUBH59bL4JbSbbTK1K22dWHymWMGaEh2sQCDOUw+OQxozYV/YdBb91leK2NbuSrE2BRamwgaYw==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -738,8 +824,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.18.10: - resolution: {integrity: sha512-otMdmSmkMe+pmiP/bZBjfphyAsTsngyT9RCYwoFzqrveAbux9nYitDTpdgToG0Z0U55+PnH654gCH2GQ1aB6Yw==} + /@esbuild/linux-ia32@0.18.4: + resolution: {integrity: sha512-4ksIqFwhq7OExty7Sl1n0vqQSCqTG4sU6i99G2yuMr28CEOUZ/60N+IO9hwI8sIxBqmKmDgncE1n5CMu/3m0IA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -756,8 +842,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.18.10: - resolution: {integrity: sha512-t8tjFuON1koxskzQ4VFoh0T5UDUMiLYjwf9Wktd0tx8AoK6xgU+5ubKOpWpcnhEQ2tESS5u0v6QuN8PX/ftwcQ==} + /@esbuild/linux-loong64@0.18.4: + resolution: {integrity: sha512-bsWtoVHkGQgAsFXioDueXRiUIfSGrVkJjBBz4gcBJxXcD461cWFQFyu8Fxdj9TP+zEeqJ8C/O4LFFMBNi6Fscw==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -774,8 +860,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.18.10: - resolution: {integrity: sha512-+dUkcVzcfEJHz3HEnVpIJu8z8Wdn2n/nWMWdl6FVPFGJAVySO4g3+XPzNKFytVFwf8hPVDwYXzVcu8GMFqsqZw==} + /@esbuild/linux-mips64el@0.18.4: + resolution: {integrity: sha512-LRD9Fu8wJQgIOOV1o3nRyzrheFYjxA0C1IVWZ93eNRRWBKgarYFejd5WBtrp43cE4y4D4t3qWWyklm73Mrsd/g==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -792,8 +878,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.18.10: - resolution: {integrity: sha512-sO3PjjxEGy+PY2qkGe2gwJbXdZN9wAYpVBZWFD0AwAoKuXRkWK0/zaMQ5ekUFJDRDCRm8x5U0Axaub7ynH/wVg==} + /@esbuild/linux-ppc64@0.18.4: + resolution: {integrity: sha512-jtQgoZjM92gauVRxNaaG/TpL3Pr4WcL3Pwqi9QgdrBGrEXzB+twohQiWNSTycs6lUygakos4mm2h0B9/SHveng==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -810,8 +896,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.18.10: - resolution: {integrity: sha512-JDtdbJg3yjDeXLv4lZYE1kiTnxv73/8cbPHY9T/dUKi8rYOM/k5b3W4UJLMUksuQ6nTm5c89W1nADsql6FW75A==} + /@esbuild/linux-riscv64@0.18.4: + resolution: {integrity: sha512-7WaU/kRZG0VCV09Xdlkg6LNAsfU9SAxo6XEdaZ8ffO4lh+DZoAhGTx7+vTMOXKxa+r2w1LYDGxfJa2rcgagMRA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -828,8 +914,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.18.10: - resolution: {integrity: sha512-NLuSKcp8WckjD2a7z5kzLiCywFwBTMlIxDNuud1AUGVuwBBJSkuubp6cNjJ0p5c6CZaA3QqUGwjHJBiG1SoOFw==} + /@esbuild/linux-s390x@0.18.4: + resolution: {integrity: sha512-D19ed0xreKQvC5t+ArE2njSnm18WPpE+1fhwaiJHf+Xwqsq+/SUaV8Mx0M27nszdU+Atq1HahrgCOZCNNEASUg==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -846,8 +932,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.18.10: - resolution: {integrity: sha512-wj2KRsCsFusli+6yFgNO/zmmLslislAWryJnodteRmGej7ZzinIbMdsyp13rVGde88zxJd5vercNYK9kuvlZaQ==} + /@esbuild/linux-x64@0.18.4: + resolution: {integrity: sha512-Rx3AY1sxyiO/gvCGP00nL69L60dfmWyjKWY06ugpB8Ydpdsfi3BHW58HWC24K3CAjAPSwxcajozC2PzA9JBS1g==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -864,8 +950,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.18.10: - resolution: {integrity: sha512-pQ9QqxEPI3cVRZyUtCoZxhZK3If+7RzR8L2yz2+TDzdygofIPOJFaAPkEJ5rYIbUO101RaiYxfdOBahYexLk5A==} + /@esbuild/netbsd-x64@0.18.4: + resolution: {integrity: sha512-AaShPmN9c6w1mKRpliKFlaWcSkpBT4KOlk93UfFgeI3F3cbjzdDKGsbKnOZozmYbE1izZKLmNJiW0sFM+A5JPA==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -882,8 +968,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.18.10: - resolution: {integrity: sha512-k8GTIIW9I8pEEfoOUm32TpPMgSg06JhL5DO+ql66aLTkOQUs0TxCA67Wi7pv6z8iF8STCGcNbm3UWFHLuci+ag==} + /@esbuild/openbsd-x64@0.18.4: + resolution: {integrity: sha512-tRGvGwou3BrvHVvF8HxTqEiC5VtPzySudS9fh2jBIKpLX7HCW8jIkW+LunkFDNwhslx4xMAgh0jAHsx/iCymaQ==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -900,8 +986,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.18.10: - resolution: {integrity: sha512-vIGYJIdEI6d4JBucAx8py792G8J0GP40qSH+EvSt80A4zvGd6jph+5t1g+eEXcS2aRpgZw6CrssNCFZxTdEsxw==} + /@esbuild/sunos-x64@0.18.4: + resolution: {integrity: sha512-acORFDI95GKhmAnlH8EarBeuqoy/j3yxIU+FDB91H3+ZON+8HhTadtT450YkaMzX6lEWbhi+mjVUCj00M5yyOQ==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -918,8 +1004,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.18.10: - resolution: {integrity: sha512-kRhNcMZFGMW+ZHCarAM1ypr8OZs0k688ViUCetVCef9p3enFxzWeBg9h/575Y0nsFu0ZItluCVF5gMR2pwOEpA==} + /@esbuild/win32-arm64@0.18.4: + resolution: {integrity: sha512-1NxP+iOk8KSvS1L9SSxEvBAJk39U0GiGZkiiJGbuDF9G4fG7DSDw6XLxZMecAgmvQrwwx7yVKdNN3GgNh0UfKg==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -936,8 +1022,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.18.10: - resolution: {integrity: sha512-AR9PX1whYaYh9p0EOaKna0h48F/A101Mt/ag72+kMkkBZXPQ7cjbz2syXI/HI3OlBdUytSdHneljfjvUoqwqiQ==} + /@esbuild/win32-ia32@0.18.4: + resolution: {integrity: sha512-OKr8jze93vbgqZ/r23woWciTixUwLa976C9W7yNBujtnVHyvsL/ocYG61tsktUfJOpyIz5TsohkBZ6Lo2+PCcQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -954,8 +1040,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.18.10: - resolution: {integrity: sha512-5sTkYhAGHNRr6bVf4RM0PsscqVr6/DBYdrlMh168oph3usid3lKHcHEEHmr34iZ9GHeeg2juFOxtpl6XyC3tpw==} + /@esbuild/win32-x64@0.18.4: + resolution: {integrity: sha512-qJr3wVvcLjPFcV4AMDS3iquhBfTef2zo/jlm8RMxmiRp3Vy2HY8WMxrykJlcbCnqLXZPA0YZxZGND6eug85ogg==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1478,7 +1564,7 @@ packages: /@swc/helpers@0.5.1: resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: - tslib: 2.6.0 + tslib: 2.5.3 dev: false /@theguild/remark-mermaid@0.0.3(react@18.2.0): @@ -1649,7 +1735,7 @@ packages: grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 - semver: 7.5.3 + semver: 7.5.2 tsutils: 3.21.0(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: @@ -1951,7 +2037,7 @@ packages: debug: 4.3.4 emittery: 1.0.1 figures: 5.0.0 - globby: 13.2.0 + globby: 13.1.4 ignore-by-default: 2.1.0 indent-string: 5.0.0 is-error: 2.2.2 @@ -2040,13 +2126,13 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true - /bundle-require@4.0.1(esbuild@0.18.10): + /bundle-require@4.0.1(esbuild@0.18.4): resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.17' dependencies: - esbuild: 0.18.10 + esbuild: 0.18.4 load-tsconfig: 0.2.5 dev: true @@ -2814,7 +2900,7 @@ packages: resolution: {integrity: sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==} engines: {node: '>=14.16'} dependencies: - globby: 13.2.0 + globby: 13.1.4 graceful-fs: 4.2.11 is-glob: 4.0.3 is-path-cwd: 3.0.0 @@ -3004,34 +3090,34 @@ packages: '@esbuild/win32-x64': 0.17.19 dev: true - /esbuild@0.18.10: - resolution: {integrity: sha512-33WKo67auOXzZHBY/9DTJRo7kIvfU12S+D4sp2wIz39N88MDIaCGyCwbW01RR70pK6Iya0I74lHEpyLfFqOHPA==} + /esbuild@0.18.4: + resolution: {integrity: sha512-9rxWV/Cb2DMUXfe9aUsYtqg0KTlw146ElFH22kYeK9KVV1qT082X4lpmiKsa12ePiCcIcB686TQJxaGAa9TFvA==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.18.10 - '@esbuild/android-arm64': 0.18.10 - '@esbuild/android-x64': 0.18.10 - '@esbuild/darwin-arm64': 0.18.10 - '@esbuild/darwin-x64': 0.18.10 - '@esbuild/freebsd-arm64': 0.18.10 - '@esbuild/freebsd-x64': 0.18.10 - '@esbuild/linux-arm': 0.18.10 - '@esbuild/linux-arm64': 0.18.10 - '@esbuild/linux-ia32': 0.18.10 - '@esbuild/linux-loong64': 0.18.10 - '@esbuild/linux-mips64el': 0.18.10 - '@esbuild/linux-ppc64': 0.18.10 - '@esbuild/linux-riscv64': 0.18.10 - '@esbuild/linux-s390x': 0.18.10 - '@esbuild/linux-x64': 0.18.10 - '@esbuild/netbsd-x64': 0.18.10 - '@esbuild/openbsd-x64': 0.18.10 - '@esbuild/sunos-x64': 0.18.10 - '@esbuild/win32-arm64': 0.18.10 - '@esbuild/win32-ia32': 0.18.10 - '@esbuild/win32-x64': 0.18.10 + '@esbuild/android-arm': 0.18.4 + '@esbuild/android-arm64': 0.18.4 + '@esbuild/android-x64': 0.18.4 + '@esbuild/darwin-arm64': 0.18.4 + '@esbuild/darwin-x64': 0.18.4 + '@esbuild/freebsd-arm64': 0.18.4 + '@esbuild/freebsd-x64': 0.18.4 + '@esbuild/linux-arm': 0.18.4 + '@esbuild/linux-arm64': 0.18.4 + '@esbuild/linux-ia32': 0.18.4 + '@esbuild/linux-loong64': 0.18.4 + '@esbuild/linux-mips64el': 0.18.4 + '@esbuild/linux-ppc64': 0.18.4 + '@esbuild/linux-riscv64': 0.18.4 + '@esbuild/linux-s390x': 0.18.4 + '@esbuild/linux-x64': 0.18.4 + '@esbuild/netbsd-x64': 0.18.4 + '@esbuild/openbsd-x64': 0.18.4 + '@esbuild/sunos-x64': 0.18.4 + '@esbuild/win32-arm64': 0.18.4 + '@esbuild/win32-ia32': 0.18.4 + '@esbuild/win32-x64': 0.18.4 dev: true /escalade@3.1.1: @@ -3125,7 +3211,7 @@ packages: lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.3 + optionator: 0.9.1 strip-ansi: 6.0.1 strip-json-comments: 3.1.1 text-table: 0.2.0 @@ -3222,6 +3308,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + /execa@0.8.0: resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==} engines: {node: '>=4'} @@ -3496,8 +3586,8 @@ packages: get-intrinsic: 1.2.1 dev: true - /get-tsconfig@4.6.2: - resolution: {integrity: sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==} + /get-tsconfig@4.6.0: + resolution: {integrity: sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==} dependencies: resolve-pkg-maps: 1.0.0 dev: true @@ -3601,8 +3691,8 @@ packages: slash: 3.0.0 dev: true - /globby@13.2.0: - resolution: {integrity: sha512-jWsQfayf13NvqKUIL3Ta+CIqMnvlaIDFveWE/dpOZ9+3AMEJozsxDvKA02zync9UuvOM8rOXzsD5GqKP4OnWPQ==} + /globby@13.1.4: + resolution: {integrity: sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: dir-glob: 3.0.1 @@ -5635,16 +5725,16 @@ packages: zod: 3.21.4 dev: false - /optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + /optionator@0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 + word-wrap: 1.2.3 dev: true /os-tmpdir@1.0.2: @@ -5934,8 +6024,8 @@ packages: engines: {node: '>=6'} dev: true - /pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + /pirates@4.0.5: + resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} dev: true @@ -6352,8 +6442,8 @@ packages: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} dev: false - /rollup@3.25.3: - resolution: {integrity: sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==} + /rollup@3.25.1: + resolution: {integrity: sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -6378,7 +6468,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.6.0 + tslib: 2.5.3 dev: true /sade@1.8.1: @@ -6423,6 +6513,14 @@ packages: hasBin: true dev: true + /semver@7.5.2: + resolution: {integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /semver@7.5.3: resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} engines: {node: '>=10'} @@ -6805,7 +6903,7 @@ packages: glob: 7.1.6 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.5 ts-interface-checker: 0.1.13 dev: true @@ -6959,8 +7057,8 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib@2.6.0: - resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + /tslib@2.5.3: + resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} /tsup@7.1.0(typescript@5.1.6): resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==} @@ -6978,17 +7076,17 @@ packages: typescript: optional: true dependencies: - bundle-require: 4.0.1(esbuild@0.18.10) + bundle-require: 4.0.1(esbuild@0.18.4) cac: 6.7.14 chokidar: 3.5.3 debug: 4.3.4 - esbuild: 0.18.10 + esbuild: 0.18.4 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 postcss-load-config: 4.0.1 resolve-from: 5.0.0 - rollup: 3.25.3 + rollup: 3.25.1 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 @@ -7455,6 +7553,11 @@ packages: isexe: 2.0.0 dev: true + /word-wrap@1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: false @@ -7566,6 +7669,14 @@ packages: engines: {node: '>=12.20'} dev: true + /zod-to-json-schema@3.21.2(zod@3.21.4): + resolution: {integrity: sha512-02yfKymfmIf2rM/5LYGlyw0daEel/f3MsSGMNJZWWf44ato+Y+diFugOpDtgvEUn3cYM5oDAGWW2NHeSD4mByw==} + peerDependencies: + zod: ^3.21.4 + dependencies: + zod: 3.21.4 + dev: false + /zod-to-json-schema@3.21.3(zod@3.21.4): resolution: {integrity: sha512-09W/9oyxeF1/wWnzCb6MursW+lOzgKi91QwE7eTBbC+t/qgfuLsUVDai3lHemSQnQu/UONAcT/fv3ZnDvbTeKg==} peerDependencies: From cb390f72f208b8b7cba45f3c291351b3df55877b Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 28 Jun 2023 13:35:27 -0400 Subject: [PATCH 03/20] feat: add event emitter to tasks --- legacy/packages/core/src/events/emitters.ts | 35 ++++ legacy/packages/core/src/events/event.ts | 130 ++++++++++++++ .../{ => packages/core}/src/events/index.ts | 1 + .../{ => packages/core}/src/events/symbols.ts | 0 .../{ => packages/core}/src/events/tracker.ts | 0 legacy/packages/core/src/task.ts | 17 +- legacy/src/events/event.ts | 166 ------------------ 7 files changed, 178 insertions(+), 171 deletions(-) create mode 100644 legacy/packages/core/src/events/emitters.ts create mode 100644 legacy/packages/core/src/events/event.ts rename legacy/{ => packages/core}/src/events/index.ts (64%) rename legacy/{ => packages/core}/src/events/symbols.ts (100%) rename legacy/{ => packages/core}/src/events/tracker.ts (100%) delete mode 100644 legacy/src/events/event.ts diff --git a/legacy/packages/core/src/events/emitters.ts b/legacy/packages/core/src/events/emitters.ts new file mode 100644 index 00000000..e5a494e5 --- /dev/null +++ b/legacy/packages/core/src/events/emitters.ts @@ -0,0 +1,35 @@ +import { EventEmitter } from 'eventemitter3' + +import * as types from '@/types' +import { BaseTask } from '@/task' + +import { TaskEvent, TaskStatus } from './event' + +export class TaskEventEmitter< + TInput extends types.TaskInput = void, + TOutput extends types.TaskOutput = string +> extends EventEmitter { + protected _task: BaseTask + + constructor(task: BaseTask) { + super() + + this._task = task + } + + emit(taskStatus: string | symbol, payload: object = {}): boolean { + if (!Object.values(TaskStatus).includes(taskStatus as TaskStatus)) { + return false + } + + const event = new TaskEvent({ + payload: { + taskStatus: taskStatus as TaskStatus, + taskId: this._task.id, + taskName: this._task.nameForModel, + ...payload + } + }) + return super.emit(taskStatus, event) + } +} diff --git a/legacy/packages/core/src/events/event.ts b/legacy/packages/core/src/events/event.ts new file mode 100644 index 00000000..b41912dd --- /dev/null +++ b/legacy/packages/core/src/events/event.ts @@ -0,0 +1,130 @@ +import { defaultIDGeneratorFn } from '@/utils' + +/** + * Payload of an event. + */ +interface EventPayload { + [key: string]: unknown +} + +/** + * Data required to create a new Event object. + */ +interface EventData { + id?: string + timestamp?: Date + payload?: T + version?: number + type?: string +} + +/** + * Events that occur within the library (should be treated as immutable). + */ +export class Event { + public readonly id: string + public readonly timestamp: Date + public readonly payload?: T + public readonly version: number + + constructor(data: EventData = {}) { + this.id = defaultIDGeneratorFn() + this.timestamp = data.timestamp ?? new Date() + this.payload = data.payload + ? JSON.parse(JSON.stringify(data.payload)) + : undefined + this.version = data.version ?? 1 + } + + /** + * Converts a JSON string representation of an event back into an Event object. + */ + static fromJSON(json: string): Event { + const data = JSON.parse(json) + data.timestamp = new Date(data.timestamp) + let Type + switch (data.type) { + case 'TaskEvent': + Type = TaskEvent + break + case 'Event': + Type = Event + break + default: + throw new Error(`Unknown event type: ${data.type}`) + } + + return new Type(data) + } + + toJSON(): string { + return JSON.stringify({ + id: this.id, + timestamp: this.timestamp.toISOString(), + payload: this.payload, + version: this.version, + type: this.constructor.name + }) + } + + toString(): string { + return `Event { id: ${ + this.id + }, timestamp: ${this.timestamp.toISOString()}, payload: ${JSON.stringify( + this.payload + )} }` + } +} + +/** + * Payload of a task event. + */ +interface TaskEventPayload extends EventPayload { + taskName: string + taskId: string + taskStatus: TaskStatus + taskInputs?: any // Consider replacing 'any' with the actual task data type if possible., + taskOutput?: any // Consider replacing 'any' with the actual task data type if possible., + taskParent?: string +} + +/** + * Status of a task. + */ +export enum TaskStatus { + SUCCEEDED = 'SUCCEEDED', + FAILED = 'FAILED', + RETRYING = 'RETRYING', + SKIPPED = 'SKIPPED', + RUNNING = 'RUNNING', + CANCELLED = 'CANCELLED' +} + +/** + * Events that occur within the library related to tasks. + */ +export class TaskEvent extends Event { + get name(): string { + return this.payload?.taskName ?? '' + } + + get taskId(): string { + return this.payload?.taskId ?? '' + } + + get status(): TaskStatus { + return this.payload?.taskStatus ?? TaskStatus.RUNNING + } + + get inputs(): any { + return this.payload?.taskInputs ?? '' + } + + get output(): any { + return this.payload?.taskOutput ?? '' + } + + get parent(): string { + return this.payload?.taskParent ?? 'root' + } +} diff --git a/legacy/src/events/index.ts b/legacy/packages/core/src/events/index.ts similarity index 64% rename from legacy/src/events/index.ts rename to legacy/packages/core/src/events/index.ts index b15f5f24..c52faae3 100644 --- a/legacy/src/events/index.ts +++ b/legacy/packages/core/src/events/index.ts @@ -1,2 +1,3 @@ +export * from './emitters' export * from './event' export * from './tracker' diff --git a/legacy/src/events/symbols.ts b/legacy/packages/core/src/events/symbols.ts similarity index 100% rename from legacy/src/events/symbols.ts rename to legacy/packages/core/src/events/symbols.ts diff --git a/legacy/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts similarity index 100% rename from legacy/src/events/tracker.ts rename to legacy/packages/core/src/events/tracker.ts diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index a01573df..f950d098 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -1,17 +1,18 @@ +import { EventEmitter } from 'eventemitter3' import pRetry, { FailedAttemptError } from 'p-retry' import QuickLRU from 'quick-lru' import { ZodType } from 'zod' -import * as errors from './errors' -import * as types from './types' import type { Agentic } from './agentic' import { SKIP_HOOKS } from './constants' -import { TaskEvent, TaskStatus } from './events' +import * as errors from './errors' +import { TaskEvent, TaskEventEmitter, TaskStatus } from './events' import { HumanFeedbackMechanismCLI, HumanFeedbackOptions, HumanFeedbackType } from './human-feedback' +import * as types from './types' import { defaultIDGeneratorFn, isValidTaskIdentifier } from './utils' /** @@ -37,6 +38,7 @@ export abstract class BaseTask< protected _timeoutMs?: number protected _retryConfig: types.RetryConfig protected _cacheConfig: types.CacheConfig + protected _eventEmitter: TaskEventEmitter protected _preHooks: Array<{ hook: types.TaskBeforeCallHook @@ -72,6 +74,8 @@ export abstract class BaseTask< this._id = options.id ?? this._agentic?.idGeneratorFn() ?? defaultIDGeneratorFn() + + this._eventEmitter = new TaskEventEmitter(this) } public get agentic(): Agentic { @@ -90,6 +94,10 @@ export abstract class BaseTask< return this._agentic.logger } + public get eventEmitter(): EventEmitter { + return this._eventEmitter + } + public abstract get inputSchema(): ZodType public abstract get outputSchema(): ZodType @@ -338,8 +346,7 @@ export abstract class BaseTask< ...this._retryConfig, onFailedAttempt: async (err: FailedAttemptError) => { this._logger.warn( - `Task error "${this.nameForHuman}" failed attempt ${ - err.attemptNumber + `Task error "${this.nameForHuman}" failed attempt ${err.attemptNumber }${input ? ': ' + JSON.stringify(input) : ''}`, err ) diff --git a/legacy/src/events/event.ts b/legacy/src/events/event.ts deleted file mode 100644 index ddc1c8c3..00000000 --- a/legacy/src/events/event.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { Jsonifiable } from '@/types' -import { defaultIDGeneratorFn } from '@/utils' - -/** - * Payload of an event. - */ -interface EventPayload { - [key: string]: unknown -} - -/** - * Data required to create a new Event object. - */ -interface EventData { - id?: string - timestamp?: Date - payload?: EventPayload - version?: number -} - -/** - * Events that occur within the library (should be treated as immutable). - */ -export class Event { - public readonly id: string - public readonly timestamp: Date - public readonly payload?: EventPayload - public readonly version: number - - constructor(data: EventData = {}) { - this.id = defaultIDGeneratorFn() - this.timestamp = data.timestamp ?? new Date() - - this.payload = data.payload - ? JSON.parse(JSON.stringify(data.payload)) - : undefined - this.version = data.version ?? 1 // Default to version 1 if not provided... - } - - /** - * Converts a JSON string representation of an event back into an Event object. - */ - static fromJSON(json: string): Event { - const data = JSON.parse(json) - - // Convert the timestamp back into a Date object, since `JSON.parse()` will have turned it into a string: - data.timestamp = new Date(data.timestamp) - const event = new Event(data) - return event - } - - /** - * Converts the event to a JSON string representation. - * - * @returns JSON representation - */ - toJSON(): string { - return JSON.stringify({ - id: this.id, - timestamp: this.timestamp.toISOString(), - payload: this.payload, - version: this.version - }) - } - - /** - * Converts the event to a human-readable string representation suitable for logging. - * - * @returns string representation - */ - toString(): string { - return `Event { id: ${ - this.id - }, timestamp: ${this.timestamp.toISOString()}, payload: ${JSON.stringify( - this.payload - )} }` - } -} - -/** - * Payload of a task event. - */ -interface TaskEventPayload extends EventPayload { - taskName: string - taskId: string - taskStatus: TaskStatus - taskInputs: any // Consider replacing 'any' with the actual task data type if possible., - taskOutput?: any // Consider replacing 'any' with the actual task data type if possible., - taskParent?: string -} - -/** - * Data required to create a new TaskEvent object. - */ -interface TaskEventData extends EventData { - payload?: TaskEventPayload -} - -/** - * Status of a task. - */ -export enum TaskStatus { - SUCCEEDED = 'SUCCEEDED', - FAILED = 'FAILED', - RETRYING = 'RETRYING', - SKIPPED = 'SKIPPED', - RUNNING = 'RUNNING', - CANCELLED = 'CANCELLED' -} - -/** - * Events that occur within the library related to tasks. - */ -export class TaskEvent extends Event { - public readonly name: string - public readonly taskId: string - public readonly status: TaskStatus - public readonly inputs: any - public readonly output?: Jsonifiable - public readonly parent?: string - - constructor(data: TaskEventData = {}) { - super(data) - - this.name = data.payload?.taskName ?? '' - this.taskId = data.payload?.taskId ?? '' - this.status = data.payload?.taskStatus ?? TaskStatus.RUNNING - this.inputs = data.payload?.taskData ?? '' - this.output = data.payload?.taskOutput ?? '' - this.parent = data.payload?.taskParent ?? 'root' - } - - /** - * Converts a JSON string representation of a task event back into a TaskEvent object. - */ - static fromJSON(json: string): TaskEvent { - const data = JSON.parse(json) - // Convert the timestamp back into a Date object, since `JSON.parse()` will have turned it into a string: - data.timestamp = new Date(data.timestamp) - const taskEvent = new TaskEvent(data) - return taskEvent - } - - /** - * Converts the task event to a JSON string representation. - */ - toJSON(): string { - return JSON.stringify({ - id: this.id, - timestamp: this.timestamp.toISOString(), - payload: this.payload, - version: this.version - }) - } - - /** - * Converts the task event to a human-readable string representation suitable for logging. - */ - toString(): string { - return `TaskEvent { id: ${ - this.id - }, timestamp: ${this.timestamp.toISOString()}, payload: ${JSON.stringify( - this.payload - )} }` - } -} From 745759fdc71f230161e446238c892d9bf499e225 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 28 Jun 2023 13:45:33 -0400 Subject: [PATCH 04/20] refactor: handle tracking in emitter --- legacy/packages/core/src/events/emitters.ts | 6 ++++- legacy/packages/core/src/task.ts | 29 +++++++++------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/legacy/packages/core/src/events/emitters.ts b/legacy/packages/core/src/events/emitters.ts index e5a494e5..5ba5abf6 100644 --- a/legacy/packages/core/src/events/emitters.ts +++ b/legacy/packages/core/src/events/emitters.ts @@ -1,6 +1,7 @@ import { EventEmitter } from 'eventemitter3' import * as types from '@/types' +import type { Agentic } from '@/agentic' import { BaseTask } from '@/task' import { TaskEvent, TaskStatus } from './event' @@ -9,12 +10,14 @@ export class TaskEventEmitter< TInput extends types.TaskInput = void, TOutput extends types.TaskOutput = string > extends EventEmitter { + protected _agentic: Agentic protected _task: BaseTask - constructor(task: BaseTask) { + constructor(task: BaseTask, agentic: Agentic) { super() this._task = task + this._agentic = agentic } emit(taskStatus: string | symbol, payload: object = {}): boolean { @@ -30,6 +33,7 @@ export class TaskEventEmitter< ...payload } }) + this._agentic.taskTracker.addEvent(event) return super.emit(taskStatus, event) } } diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index f950d098..db9060de 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -6,7 +6,7 @@ import { ZodType } from 'zod' import type { Agentic } from './agentic' import { SKIP_HOOKS } from './constants' import * as errors from './errors' -import { TaskEvent, TaskEventEmitter, TaskStatus } from './events' +import { TaskEventEmitter, TaskStatus } from './events' import { HumanFeedbackMechanismCLI, HumanFeedbackOptions, @@ -75,7 +75,10 @@ export abstract class BaseTask< this._id = options.id ?? this._agentic?.idGeneratorFn() ?? defaultIDGeneratorFn() - this._eventEmitter = new TaskEventEmitter(this) + this._eventEmitter = new TaskEventEmitter( + this, + this._agentic + ) } public get agentic(): Agentic { @@ -281,14 +284,10 @@ export abstract class BaseTask< } } - const taskEvent = new TaskEvent({ - payload: { - taskStatus: TaskStatus.RUNNING, - taskInputs: input, - ...ctx.metadata - } + this._eventEmitter.emit(TaskStatus.RUNNING, { + taskInputs: input, + ...ctx.metadata }) - this._agentic.taskTracker.addEvent(taskEvent) for (const { hook: preHook } of this._preHooks) { const preHookResult = await preHook(ctx) @@ -398,15 +397,11 @@ export abstract class BaseTask< // ctx.tracker.setOutput(stringifyForDebugging(result, { maxLength: 100 })) - const taskEvent2 = new TaskEvent({ - payload: { - taskInputs: input, - taskStatus: TaskStatus.SUCCEEDED, - taskOutput: result, - ...ctx.metadata - } + this._eventEmitter.emit(TaskStatus.SUCCEEDED, { + taskInputs: input, + taskOutput: result, + ...ctx.metadata }) - this._agentic.taskTracker.addEvent(taskEvent2) return { result, From 9191cfd281f25e69a3c1b9f40f743af3a40194f5 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 28 Jun 2023 13:58:45 -0400 Subject: [PATCH 05/20] feat: add emitter to agentic instance --- legacy/packages/core/src/agentic.ts | 7 +++++++ legacy/packages/core/src/events/emitters.ts | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/legacy/packages/core/src/agentic.ts b/legacy/packages/core/src/agentic.ts index 205f73d6..cbae5be4 100644 --- a/legacy/packages/core/src/agentic.ts +++ b/legacy/packages/core/src/agentic.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from 'eventemitter3' import defaultKy from 'ky' import { SetOptional } from 'type-fest' @@ -14,6 +15,7 @@ export class Agentic { protected _ky: types.KyInstance protected _logger: types.Logger protected _taskTracker: TerminalTaskTracker + protected _eventEmitter: EventEmitter protected _openai?: types.openai.OpenAIClient protected _anthropic?: types.anthropic.Client @@ -52,6 +54,7 @@ export class Agentic { this._ky = opts.ky ?? defaultKy this._logger = opts.logger ?? defaultLogger this._taskTracker = opts.taskTracker ?? defaultTaskTracker + this._eventEmitter = new EventEmitter() this._openaiModelDefaults = { provider: 'openai', @@ -106,6 +109,10 @@ export class Agentic { return this._taskTracker } + public get eventEmitter(): EventEmitter { + return this._eventEmitter + } + public get idGeneratorFn(): types.IDGeneratorFunction { return this._idGeneratorFn } diff --git a/legacy/packages/core/src/events/emitters.ts b/legacy/packages/core/src/events/emitters.ts index 5ba5abf6..1a35beda 100644 --- a/legacy/packages/core/src/events/emitters.ts +++ b/legacy/packages/core/src/events/emitters.ts @@ -34,6 +34,10 @@ export class TaskEventEmitter< } }) this._agentic.taskTracker.addEvent(event) + + const name = `${this._task.nameForModel}:${String(taskStatus)}` + this._agentic.eventEmitter.emit(name, event) + return super.emit(taskStatus, event) } } From b140c974b8f02000a145194fcd8b41b777a6a832 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 29 Jun 2023 11:47:59 -0400 Subject: [PATCH 06/20] feat: improve type safety for event emitting --- legacy/examples/event-handling.ts | 22 +++++++++++++++++++++ legacy/packages/core/src/events/emitters.ts | 20 +++++++++++++------ legacy/packages/core/src/events/event.ts | 16 ++++++++------- legacy/packages/core/src/events/tracker.ts | 4 ++-- legacy/packages/core/src/index.ts | 5 +++-- legacy/packages/core/src/task.ts | 5 ++--- 6 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 legacy/examples/event-handling.ts diff --git a/legacy/examples/event-handling.ts b/legacy/examples/event-handling.ts new file mode 100644 index 00000000..acd25db9 --- /dev/null +++ b/legacy/examples/event-handling.ts @@ -0,0 +1,22 @@ +import 'dotenv/config' +import { OpenAIClient } from 'openai-fetch' +import { z } from 'zod' + +import { Agentic, TaskStatus } from '@/index' + +async function main() { + const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! }) + const $ = new Agentic({ openai }) + + const ai = $.gpt4(`generate fake data`).output( + z.object({ foo: z.string(), bar: z.number() }) + ) + + ai.eventEmitter.on(TaskStatus.COMPLETED, (event) => { + console.log('Task completed successfully:', event) + }) + + ai.call() +} + +main() diff --git a/legacy/packages/core/src/events/emitters.ts b/legacy/packages/core/src/events/emitters.ts index 1a35beda..a05746a2 100644 --- a/legacy/packages/core/src/events/emitters.ts +++ b/legacy/packages/core/src/events/emitters.ts @@ -20,23 +20,31 @@ export class TaskEventEmitter< this._agentic = agentic } + on( + takStatus: T, + fn: (event: TaskEvent) => void, + context?: any + ): this { + return super.on(takStatus, fn, context) + } + emit(taskStatus: string | symbol, payload: object = {}): boolean { if (!Object.values(TaskStatus).includes(taskStatus as TaskStatus)) { - return false + throw new Error(`Invalid task status: ${String(taskStatus)}`) } - const event = new TaskEvent({ + const { id, nameForModel } = this._task + const event = new TaskEvent({ payload: { taskStatus: taskStatus as TaskStatus, - taskId: this._task.id, - taskName: this._task.nameForModel, + taskId: id, + taskName: nameForModel, ...payload } }) this._agentic.taskTracker.addEvent(event) - const name = `${this._task.nameForModel}:${String(taskStatus)}` - this._agentic.eventEmitter.emit(name, event) + this._agentic.eventEmitter.emit(taskStatus, event) return super.emit(taskStatus, event) } diff --git a/legacy/packages/core/src/events/event.ts b/legacy/packages/core/src/events/event.ts index b41912dd..63bc4b41 100644 --- a/legacy/packages/core/src/events/event.ts +++ b/legacy/packages/core/src/events/event.ts @@ -3,14 +3,14 @@ import { defaultIDGeneratorFn } from '@/utils' /** * Payload of an event. */ -interface EventPayload { +export interface EventPayload { [key: string]: unknown } /** * Data required to create a new Event object. */ -interface EventData { +export interface EventData { id?: string timestamp?: Date payload?: T @@ -79,12 +79,12 @@ export class Event { /** * Payload of a task event. */ -interface TaskEventPayload extends EventPayload { +export interface TaskEventPayload extends EventPayload { taskName: string taskId: string taskStatus: TaskStatus - taskInputs?: any // Consider replacing 'any' with the actual task data type if possible., - taskOutput?: any // Consider replacing 'any' with the actual task data type if possible., + taskInputs?: TInput + taskOutput?: TOutput taskParent?: string } @@ -92,7 +92,7 @@ interface TaskEventPayload extends EventPayload { * Status of a task. */ export enum TaskStatus { - SUCCEEDED = 'SUCCEEDED', + COMPLETED = 'COMPLETED', FAILED = 'FAILED', RETRYING = 'RETRYING', SKIPPED = 'SKIPPED', @@ -103,7 +103,9 @@ export enum TaskStatus { /** * Events that occur within the library related to tasks. */ -export class TaskEvent extends Event { +export class TaskEvent extends Event< + TaskEventPayload +> { get name(): string { return this.payload?.taskName ?? '' } diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index b3023554..420ae6ae 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -160,7 +160,7 @@ export class TerminalTaskTracker { status: TaskStatus ): [string, (text: string) => string] { switch (status) { - case TaskStatus.SUCCEEDED: + case TaskStatus.COMPLETED: return [SYMBOLS.CIRCLE, green] case TaskStatus.FAILED: return [SYMBOLS.CROSS, red] @@ -206,7 +206,7 @@ export class TerminalTaskTracker { } if (output) { - if (status === TaskStatus.SUCCEEDED) { + if (status === TaskStatus.COMPLETED) { const formattedOutput = this.stringify(output) line += indent + diff --git a/legacy/packages/core/src/index.ts b/legacy/packages/core/src/index.ts index 4df46984..3287cc3d 100644 --- a/legacy/packages/core/src/index.ts +++ b/legacy/packages/core/src/index.ts @@ -1,9 +1,10 @@ export * from './agentic' -export * from './task' export * from './constants' export * from './errors' -export * from './tokenizer' +export * from './events' export * from './human-feedback' +export * from './task' +export * from './tokenizer' export * from './llms' export * from './services' diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index db9060de..5299439e 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'eventemitter3' import pRetry, { FailedAttemptError } from 'p-retry' import QuickLRU from 'quick-lru' import { ZodType } from 'zod' @@ -97,7 +96,7 @@ export abstract class BaseTask< return this._agentic.logger } - public get eventEmitter(): EventEmitter { + public get eventEmitter(): TaskEventEmitter { return this._eventEmitter } @@ -397,7 +396,7 @@ export abstract class BaseTask< // ctx.tracker.setOutput(stringifyForDebugging(result, { maxLength: 100 })) - this._eventEmitter.emit(TaskStatus.SUCCEEDED, { + this._eventEmitter.emit(TaskStatus.COMPLETED, { taskInputs: input, taskOutput: result, ...ctx.metadata From 42bf50eee39562a68e7c92a75be7c0f97b55c0dc Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 29 Jun 2023 12:27:04 -0400 Subject: [PATCH 07/20] fix: supply generic params for TaskEvent where missing --- legacy/packages/core/src/events/event.ts | 2 +- legacy/packages/core/src/events/tracker.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/legacy/packages/core/src/events/event.ts b/legacy/packages/core/src/events/event.ts index 63bc4b41..85d85365 100644 --- a/legacy/packages/core/src/events/event.ts +++ b/legacy/packages/core/src/events/event.ts @@ -45,7 +45,7 @@ export class Event { let Type switch (data.type) { case 'TaskEvent': - Type = TaskEvent + Type = TaskEvent break case 'Event': Type = Event diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index 420ae6ae..37291563 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -136,7 +136,7 @@ export class TerminalTaskTracker { }, INACTIVITY_THRESHOLD) } - addEvent(event: TaskEvent) { + addEvent(event: TaskEvent) { const { parent = 'root', taskId, name, status, inputs, output } = event if (!this.events[parent]) { this.events[parent] = [] From 9ddb026ab9bfce3e66e1786a8f32688c7c0f1b6b Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 29 Jun 2023 17:04:11 -0400 Subject: [PATCH 08/20] feat: also buffer stderr --- legacy/packages/core/src/events/tracker.ts | 74 ++++++++++++++++------ 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index 37291563..67fbf695 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -8,12 +8,30 @@ import { SPACE } from '@/constants' import { TaskEvent, TaskStatus } from './event' import { SYMBOLS } from './symbols' -const consoleBuffer: any[] = [] +const MAGIC_STRING = '__INSIDE_TRACKER__' // Define a unique "magic" string -const originalWrite = process.stdout.write +const stdoutBuffer: any[] = [] +const stderrBuffer: any[] = [] + +const originalStdoutWrite = process.stdout.write process.stdout.write = function (str: any) { - consoleBuffer.push(str) - return originalWrite.call(process.stdout, str) + stdoutBuffer.push(str) + return originalStdoutWrite.call(process.stdout, str) +} + +const originalStderrWrite = process.stderr.write +process.stderr.write = function (str: any) { + if (str.startsWith(MAGIC_STRING)) { + // This write is from inside the tracker, remove the magic string and write to stderr: + return originalStderrWrite.call( + process.stderr, + str.replace(MAGIC_STRING, '') + ) + } else { + // This write is from outside the tracker, add it to stderrBuffer and write to stderr: + stderrBuffer.push(str) + return originalStderrWrite.call(process.stderr, str) + } } const SPINNER_INTERVAL = 100 @@ -89,16 +107,27 @@ export class TerminalTaskTracker { // Remove the keypress listener: process.stdin.off('keypress', this.handleKeyPress) - process.stderr.write('\n') - process.stderr.write('\n') - process.stderr.write('Completed all tasks.\n') - process.stderr.write('\n') - process.stderr.write('stdout:\n') - process.stderr.write('\n') - process.stderr.write(consoleBuffer.join('')) + const finalLines = [ + '', + '', + 'Completed all tasks.', + '', + 'stdout:', + '', + stdoutBuffer.join(''), + '', + '', + 'stderr:', + '', + stderrBuffer.join(''), + '', + '' + ] + this.writeWithMagicString(finalLines) - // Restore the original `process.stdout.write()` function: - process.stdout.write = originalWrite + // Restore the original `process.stdout.write()` and `process.stderr.write()` functions: + process.stdout.write = originalStdoutWrite + process.stderr.write = originalStderrWrite // Pause the reading of stdin so that the Node.js process will exit once done: process.stdin.pause() @@ -240,10 +269,19 @@ export class TerminalTaskTracker { } } - writeToConsole(lines: string[]) { - if (lines.length > 0) { - process.stderr.write(lines.join('\n')) + private writeWithMagicString(content: string | string[]) { + let output + if (Array.isArray(content)) { + if (content.length === 0) { + return + } + + output = content.join('\n') + } else { + output = content } + + process.stderr.write(MAGIC_STRING + output) } render() { @@ -251,10 +289,10 @@ export class TerminalTaskTracker { const lines = this.renderTree('root') if (this.renderTasks) { this.clearPreviousRender(lines.length + 1) - this.writeToConsole(lines) + this.writeWithMagicString(lines) } else { this.clearPreviousRender(lines.length + 1) - this.writeToConsole(consoleBuffer) + this.writeWithMagicString(stdoutBuffer) } } } From c4ecea1ff85eda190d1b5832c31f046eff2c1b43 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 29 Jun 2023 20:45:03 -0400 Subject: [PATCH 09/20] refactor: move functionality to class from module --- legacy/packages/core/src/agentic.ts | 4 +- legacy/packages/core/src/events/tracker.ts | 74 +++++++++++----------- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/legacy/packages/core/src/agentic.ts b/legacy/packages/core/src/agentic.ts index cbae5be4..110e378c 100644 --- a/legacy/packages/core/src/agentic.ts +++ b/legacy/packages/core/src/agentic.ts @@ -4,7 +4,7 @@ import { SetOptional } from 'type-fest' import * as types from './types' import { DEFAULT_OPENAI_MODEL } from './constants' -import { TerminalTaskTracker, defaultTaskTracker } from './events' +import { TerminalTaskTracker } from './events' import { HumanFeedbackOptions, HumanFeedbackType } from './human-feedback' import { HumanFeedbackMechanismCLI } from './human-feedback/cli' import { OpenAIChatCompletion } from './llms/openai' @@ -53,7 +53,7 @@ export class Agentic { this._ky = opts.ky ?? defaultKy this._logger = opts.logger ?? defaultLogger - this._taskTracker = opts.taskTracker ?? defaultTaskTracker + this._taskTracker = opts.taskTracker ?? new TerminalTaskTracker() this._eventEmitter = new EventEmitter() this._openaiModelDefaults = { diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index 67fbf695..1d8f8c1f 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -10,31 +10,7 @@ import { SYMBOLS } from './symbols' const MAGIC_STRING = '__INSIDE_TRACKER__' // Define a unique "magic" string -const stdoutBuffer: any[] = [] -const stderrBuffer: any[] = [] - -const originalStdoutWrite = process.stdout.write -process.stdout.write = function (str: any) { - stdoutBuffer.push(str) - return originalStdoutWrite.call(process.stdout, str) -} - -const originalStderrWrite = process.stderr.write -process.stderr.write = function (str: any) { - if (str.startsWith(MAGIC_STRING)) { - // This write is from inside the tracker, remove the magic string and write to stderr: - return originalStderrWrite.call( - process.stderr, - str.replace(MAGIC_STRING, '') - ) - } else { - // This write is from outside the tracker, add it to stderrBuffer and write to stderr: - stderrBuffer.push(str) - return originalStderrWrite.call(process.stderr, str) - } -} - -const SPINNER_INTERVAL = 100 +const SPINNER_INTERVAL = 1000 const INACTIVITY_THRESHOLD = 2000 // 2 seconds function getSpinnerSymbol() { @@ -44,12 +20,17 @@ function getSpinnerSymbol() { } export class TerminalTaskTracker { - private events: Record = { root: [] } - private interval: NodeJS.Timeout | null = null - private inactivityTimeout: NodeJS.Timeout | null = null - private truncateOutput = false - private renderTasks = true - private outputs: Array = [] + protected events: Record = { root: [] } + protected interval: NodeJS.Timeout | null = null + protected inactivityTimeout: NodeJS.Timeout | null = null + protected truncateOutput = false + protected renderTasks = true + protected outputs: Array = [] + + private stdoutBuffer: any[] = [] + private stderrBuffer: any[] = [] + private originalStdoutWrite = process.stdout.write + private originalStderrWrite = process.stderr.write constructor() { if (!process.stderr.isTTY) { @@ -57,6 +38,25 @@ export class TerminalTaskTracker { return } + process.stdout.write = (str: any) => { + this.stdoutBuffer.push(str) + return this.originalStdoutWrite.call(process.stdout, str) + } + + process.stderr.write = (str: any) => { + if (str.startsWith(MAGIC_STRING)) { + // This write is from inside the tracker, remove the magic string and write to stderr: + return this.originalStderrWrite.call( + process.stderr, + str.replace(MAGIC_STRING, '') + ) + } else { + // This write is from outside the tracker, add it to stderrBuffer and write to stderr: + this.stderrBuffer.push(str) + return this.originalStderrWrite.call(process.stderr, str) + } + } + this.start() } @@ -114,20 +114,20 @@ export class TerminalTaskTracker { '', 'stdout:', '', - stdoutBuffer.join(''), + this.stdoutBuffer.join(''), '', '', 'stderr:', '', - stderrBuffer.join(''), + this.stderrBuffer.join(''), '', '' ] this.writeWithMagicString(finalLines) // Restore the original `process.stdout.write()` and `process.stderr.write()` functions: - process.stdout.write = originalStdoutWrite - process.stderr.write = originalStderrWrite + process.stdout.write = this.originalStdoutWrite + process.stderr.write = this.originalStderrWrite // Pause the reading of stdin so that the Node.js process will exit once done: process.stdin.pause() @@ -292,9 +292,7 @@ export class TerminalTaskTracker { this.writeWithMagicString(lines) } else { this.clearPreviousRender(lines.length + 1) - this.writeWithMagicString(stdoutBuffer) + this.writeWithMagicString(this.stdoutBuffer) } } } - -export const defaultTaskTracker = new TerminalTaskTracker() From ac5eba853ae047bf1666acccb9996894a0fbf282 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 29 Jun 2023 22:14:08 -0400 Subject: [PATCH 10/20] fix: handle ANSI escapes and uint8 utf-8 writes --- legacy/packages/core/src/events/tracker.ts | 75 +++++++++++++++------- legacy/packages/core/src/task.ts | 9 ++- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index 1d8f8c1f..82d05688 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -10,7 +10,10 @@ import { SYMBOLS } from './symbols' const MAGIC_STRING = '__INSIDE_TRACKER__' // Define a unique "magic" string -const SPINNER_INTERVAL = 1000 +// eslint-disable-next-line no-control-regex +const RE_ANSI_ESCAPES = /\x1b\[[0-9;]*[A-Za-z]/ // cursor movement, screen clearing, etc. + +const SPINNER_INTERVAL = 100 // 100ms const INACTIVITY_THRESHOLD = 2000 // 2 seconds function getSpinnerSymbol() { @@ -19,6 +22,9 @@ function getSpinnerSymbol() { ] } +const originalStdoutWrite = process.stdout.write +const originalStderrWrite = process.stderr.write + export class TerminalTaskTracker { protected events: Record = { root: [] } protected interval: NodeJS.Timeout | null = null @@ -26,11 +32,10 @@ export class TerminalTaskTracker { protected truncateOutput = false protected renderTasks = true protected outputs: Array = [] + protected renderingPaused = false - private stdoutBuffer: any[] = [] - private stderrBuffer: any[] = [] - private originalStdoutWrite = process.stdout.write - private originalStderrWrite = process.stderr.write + private stdoutBuffer: string[] = [] + private stderrBuffer: string[] = [] constructor() { if (!process.stderr.isTTY) { @@ -38,33 +43,40 @@ export class TerminalTaskTracker { return } - process.stdout.write = (str: any) => { - this.stdoutBuffer.push(str) - return this.originalStdoutWrite.call(process.stdout, str) + process.stdout.write = (buffer: string | Uint8Array) => { + if (buffer instanceof Uint8Array) { + buffer = Buffer.from(buffer).toString('utf-8') + } + + this.stdoutBuffer.push(buffer) + return originalStdoutWrite.call(process.stdout, buffer) } - process.stderr.write = (str: any) => { - if (str.startsWith(MAGIC_STRING)) { + process.stderr.write = (buffer: string | Uint8Array) => { + if (buffer instanceof Uint8Array) { + buffer = Buffer.from(buffer).toString('utf-8') + } + + if (typeof buffer === 'string' && buffer.startsWith(MAGIC_STRING)) { // This write is from inside the tracker, remove the magic string and write to stderr: - return this.originalStderrWrite.call( + return originalStderrWrite.call( process.stderr, - str.replace(MAGIC_STRING, '') + buffer.replace(MAGIC_STRING, '') ) } else { - // This write is from outside the tracker, add it to stderrBuffer and write to stderr: - this.stderrBuffer.push(str) - return this.originalStderrWrite.call(process.stderr, str) + if (!RE_ANSI_ESCAPES.test(buffer)) { + // If an ANSI escape sequence is written to stderr, it will mess up the output, so we need to write it to stdout instead: + // This write is from outside the tracker, add it to stderrBuffer and write to stderr: + this.stderrBuffer.push(buffer) + } + + return originalStderrWrite.call(process.stderr, buffer) } } this.start() } - renderOutput() { - const output = this.outputs.join('') - process.stderr.write(output) - } - handleKeyPress = (str, key) => { if (key.ctrl && key.name === 'c') { process.exit() @@ -107,6 +119,10 @@ export class TerminalTaskTracker { // Remove the keypress listener: process.stdin.off('keypress', this.handleKeyPress) + // Restore the original `process.stdout.write()` and `process.stderr.write()` functions: + process.stdout.write = originalStdoutWrite + process.stderr.write = originalStderrWrite + const finalLines = [ '', '', @@ -123,16 +139,22 @@ export class TerminalTaskTracker { '', '' ] - this.writeWithMagicString(finalLines) - // Restore the original `process.stdout.write()` and `process.stderr.write()` functions: - process.stdout.write = this.originalStdoutWrite - process.stderr.write = this.originalStderrWrite + process.stderr.write(finalLines.join('\n')) // Pause the reading of stdin so that the Node.js process will exit once done: process.stdin.pause() } + pause() { + this.renderingPaused = true + } + + resume() { + this.renderingPaused = false + this.render() + } + stringify(value: any) { if (this.truncateOutput) { const json = JSON.stringify(value) @@ -271,6 +293,7 @@ export class TerminalTaskTracker { private writeWithMagicString(content: string | string[]) { let output + if (Array.isArray(content)) { if (content.length === 0) { return @@ -285,6 +308,10 @@ export class TerminalTaskTracker { } render() { + if (this.renderingPaused) { + return // Do not render if paused + } + this.clearAndSetCursorPosition() const lines = this.renderTree('root') if (this.renderTasks) { diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index 5299439e..eb4ce053 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -2,16 +2,16 @@ import pRetry, { FailedAttemptError } from 'p-retry' import QuickLRU from 'quick-lru' import { ZodType } from 'zod' +import * as errors from './errors' +import * as types from './types' import type { Agentic } from './agentic' import { SKIP_HOOKS } from './constants' -import * as errors from './errors' import { TaskEventEmitter, TaskStatus } from './events' import { HumanFeedbackMechanismCLI, HumanFeedbackOptions, HumanFeedbackType } from './human-feedback' -import * as types from './types' import { defaultIDGeneratorFn, isValidTaskIdentifier } from './utils' /** @@ -201,7 +201,9 @@ export abstract class BaseTask< }) this.addAfterCallHook(async (output, ctx) => { + this._agentic.taskTracker.pause() const feedback = await feedbackMechanism.interact(output) + this._agentic.taskTracker.resume() ctx.metadata = { ...ctx.metadata, feedback } if (feedback.editedOutput) { return feedback.editedOutput @@ -344,7 +346,8 @@ export abstract class BaseTask< ...this._retryConfig, onFailedAttempt: async (err: FailedAttemptError) => { this._logger.warn( - `Task error "${this.nameForHuman}" failed attempt ${err.attemptNumber + `Task error "${this.nameForHuman}" failed attempt ${ + err.attemptNumber }${input ? ': ' + JSON.stringify(input) : ''}`, err ) From e7b3c9aa14d194fb7335186f0144d2898732e4c5 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 30 Jun 2023 13:10:17 -0400 Subject: [PATCH 11/20] feat: move constants to configurable class props and add header --- legacy/packages/core/src/events/tracker.ts | 89 ++++++++++++++++------ 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index 82d05688..f5dce7a8 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -1,7 +1,7 @@ import process from 'node:process' import readline from 'node:readline' -import { bold, cyan, gray, green, red, yellow } from 'colorette' +import { bgWhite, black, bold, cyan, gray, green, red, yellow } from 'colorette' import { SPACE } from '@/constants' @@ -13,15 +13,6 @@ const MAGIC_STRING = '__INSIDE_TRACKER__' // Define a unique "magic" string // eslint-disable-next-line no-control-regex const RE_ANSI_ESCAPES = /\x1b\[[0-9;]*[A-Za-z]/ // cursor movement, screen clearing, etc. -const SPINNER_INTERVAL = 100 // 100ms -const INACTIVITY_THRESHOLD = 2000 // 2 seconds - -function getSpinnerSymbol() { - return SYMBOLS.SPINNER[ - Math.floor(Date.now() / SPINNER_INTERVAL) % SYMBOLS.SPINNER.length - ] -} - const originalStdoutWrite = process.stdout.write const originalStderrWrite = process.stderr.write @@ -30,14 +21,20 @@ export class TerminalTaskTracker { protected interval: NodeJS.Timeout | null = null protected inactivityTimeout: NodeJS.Timeout | null = null protected truncateOutput = false - protected renderTasks = true + protected viewMode = 'tasks' protected outputs: Array = [] protected renderingPaused = false + protected _spinnerInterval: number + protected _inactivityThreshold: number + private stdoutBuffer: string[] = [] private stderrBuffer: string[] = [] - constructor() { + constructor({ spinnerInterval = 100, inactivityThreshold = 2000 } = {}) { + this._spinnerInterval = spinnerInterval + this._inactivityThreshold = inactivityThreshold + if (!process.stderr.isTTY) { // If stderr is not a TTY, don't render any dynamic output... return @@ -86,15 +83,19 @@ export class TerminalTaskTracker { this.toggleOutputTruncation() } - if (key.ctrl && key.name === 'o') { - this.renderTasks = !this.renderTasks + if (key.ctrl && key.name === 'right') { + this.toggleView('next') + } + + if (key.ctrl && key.name === 'left') { + this.toggleView('prev') } } start() { this.interval = setInterval(() => { this.render() - }, SPINNER_INTERVAL) + }, this._spinnerInterval) readline.emitKeypressEvents(process.stdin) @@ -126,14 +127,14 @@ export class TerminalTaskTracker { const finalLines = [ '', '', - 'Completed all tasks.', + bgWhite(black(' Completed all tasks. ')), '', - 'stdout:', + bgWhite(black(' stdout: ')), '', this.stdoutBuffer.join(''), '', '', - 'stderr:', + bgWhite(black(' stderr: ')), '', this.stderrBuffer.join(''), '', @@ -184,7 +185,7 @@ export class TerminalTaskTracker { } else { this.startInactivityTimeout() } - }, INACTIVITY_THRESHOLD) + }, this._inactivityThreshold) } addEvent(event: TaskEvent) { @@ -219,7 +220,7 @@ export class TerminalTaskTracker { return [SYMBOLS.WARNING, yellow] case TaskStatus.RUNNING: default: - return [getSpinnerSymbol(), cyan] + return [this.getSpinnerSymbol(), cyan] } } @@ -307,19 +308,57 @@ export class TerminalTaskTracker { process.stderr.write(MAGIC_STRING + output) } + toggleView(direction) { + const viewModes = ['tasks', 'stdout', 'stderr'] + const currentIdx = viewModes.indexOf(this.viewMode) + + if (direction === 'next') { + this.viewMode = viewModes[(currentIdx + 1) % viewModes.length] + } else if (direction === 'prev') { + this.viewMode = + viewModes[(currentIdx - 1 + viewModes.length) % viewModes.length] + } + + this.render() + } + + getSpinnerSymbol() { + return SYMBOLS.SPINNER[ + Math.floor(Date.now() / this._spinnerInterval) % SYMBOLS.SPINNER.length + ] + } + + renderHeader() { + const legend = [ + 'commands', + 'ctrl+c exit', + 'ctrl+e: truncate output', + 'ctrl+left/right: switch view' + ].join(' | ') + + const header = [` Agentic: ${this.viewMode} `, ` ${legend} `, ''].join('\n') + this.writeWithMagicString(bgWhite(black(header))) + } + render() { if (this.renderingPaused) { return // Do not render if paused } this.clearAndSetCursorPosition() - const lines = this.renderTree('root') - if (this.renderTasks) { - this.clearPreviousRender(lines.length + 1) + if (this.viewMode === 'tasks') { + const lines = this.renderTree('root') + this.clearPreviousRender(lines.length + 2) + this.renderHeader() this.writeWithMagicString(lines) - } else { - this.clearPreviousRender(lines.length + 1) + } else if (this.viewMode === 'stdout') { + this.clearPreviousRender(this.stdoutBuffer.length + 2) + this.renderHeader() this.writeWithMagicString(this.stdoutBuffer) + } else if (this.viewMode === 'stderr') { + this.clearPreviousRender(this.stderrBuffer.length + 2) + this.renderHeader() + this.writeWithMagicString(this.stderrBuffer) } } } From c09c24ac668dcc9a35ffe30af1cb43b8d3f209a1 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 30 Jun 2023 14:22:02 -0400 Subject: [PATCH 12/20] refactor: rename props and update tracker output --- legacy/packages/core/src/events/tracker.ts | 141 ++++++++++++--------- legacy/packages/core/src/utils.ts | 4 + 2 files changed, 82 insertions(+), 63 deletions(-) diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index f5dce7a8..683877f3 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -4,34 +4,43 @@ import readline from 'node:readline' import { bgWhite, black, bold, cyan, gray, green, red, yellow } from 'colorette' import { SPACE } from '@/constants' +import { capitalize } from '@/utils' import { TaskEvent, TaskStatus } from './event' import { SYMBOLS } from './symbols' -const MAGIC_STRING = '__INSIDE_TRACKER__' // Define a unique "magic" string +export const MAGIC_STRING = '__INSIDE_TRACKER__' // a unique "magic" string that used to identify the output of the tracker // eslint-disable-next-line no-control-regex -const RE_ANSI_ESCAPES = /\x1b\[[0-9;]*[A-Za-z]/ // cursor movement, screen clearing, etc. +const RE_ANSI_ESCAPES = /^(\x1b\[[0-9;]*[ABCDHJK]|[\r\n])+$/ // cursor movement, screen clearing, etc. const originalStdoutWrite = process.stdout.write const originalStderrWrite = process.stderr.write +export interface TerminalTaskTrackerOptions { + spinnerInterval?: number + inactivityThreshold?: number +} + export class TerminalTaskTracker { - protected events: Record = { root: [] } - protected interval: NodeJS.Timeout | null = null - protected inactivityTimeout: NodeJS.Timeout | null = null - protected truncateOutput = false - protected viewMode = 'tasks' - protected outputs: Array = [] - protected renderingPaused = false + protected _events: Record = { root: [] } + protected _interval: NodeJS.Timeout | null = null + protected _inactivityTimeout: NodeJS.Timeout | null = null + protected _truncateOutput = false + protected _viewMode = 'tasks' + protected _outputs: Array = [] + protected _renderingPaused = false protected _spinnerInterval: number protected _inactivityThreshold: number - private stdoutBuffer: string[] = [] - private stderrBuffer: string[] = [] + private _stdoutBuffer: string[] = [] + private _stderrBuffer: string[] = [] - constructor({ spinnerInterval = 100, inactivityThreshold = 2000 } = {}) { + constructor({ + spinnerInterval = 100, + inactivityThreshold = 2_000 + }: TerminalTaskTrackerOptions = {}) { this._spinnerInterval = spinnerInterval this._inactivityThreshold = inactivityThreshold @@ -45,7 +54,10 @@ export class TerminalTaskTracker { buffer = Buffer.from(buffer).toString('utf-8') } - this.stdoutBuffer.push(buffer) + if (!this._renderingPaused) { + this._stdoutBuffer.push(buffer) + } + return originalStdoutWrite.call(process.stdout, buffer) } @@ -61,10 +73,10 @@ export class TerminalTaskTracker { buffer.replace(MAGIC_STRING, '') ) } else { - if (!RE_ANSI_ESCAPES.test(buffer)) { + if (!this._renderingPaused && !RE_ANSI_ESCAPES.test(buffer)) { // If an ANSI escape sequence is written to stderr, it will mess up the output, so we need to write it to stdout instead: // This write is from outside the tracker, add it to stderrBuffer and write to stderr: - this.stderrBuffer.push(buffer) + this._stderrBuffer.push(buffer) } return originalStderrWrite.call(process.stderr, buffer) @@ -93,7 +105,7 @@ export class TerminalTaskTracker { } start() { - this.interval = setInterval(() => { + this._interval = setInterval(() => { this.render() }, this._spinnerInterval) @@ -107,12 +119,12 @@ export class TerminalTaskTracker { } close() { - if (this.interval) { - clearInterval(this.interval) + if (this._interval) { + clearInterval(this._interval) } - if (this.inactivityTimeout) { - clearTimeout(this.inactivityTimeout) + if (this._inactivityTimeout) { + clearTimeout(this._inactivityTimeout) } process.stdin.setRawMode(false) @@ -129,15 +141,14 @@ export class TerminalTaskTracker { '', bgWhite(black(' Completed all tasks. ')), '', - bgWhite(black(' stdout: ')), - '', - this.stdoutBuffer.join(''), - '', '', bgWhite(black(' stderr: ')), '', - this.stderrBuffer.join(''), + this._stderrBuffer.join(''), '', + bgWhite(black(' stdout: ')), + '', + this._stdoutBuffer.join(''), '' ] @@ -148,16 +159,17 @@ export class TerminalTaskTracker { } pause() { - this.renderingPaused = true + this.clearAndSetCursorPosition() + this._renderingPaused = true } resume() { - this.renderingPaused = false + this._renderingPaused = false this.render() } - stringify(value: any) { - if (this.truncateOutput) { + stringify(value: any): string { + if (this._truncateOutput) { const json = JSON.stringify(value) if (json.length < 40) { return json @@ -170,14 +182,13 @@ export class TerminalTaskTracker { } toggleOutputTruncation() { - this.truncateOutput = !this.truncateOutput + this._truncateOutput = !this._truncateOutput } startInactivityTimeout() { - this.inactivityTimeout = setTimeout(() => { - // Check if all tasks are completed: - const allTasksCompleted = Object.values(this.events).every((events) => - events.every((event) => event.status !== TaskStatus.RUNNING) + this._inactivityTimeout = setTimeout(() => { + const allTasksCompleted = Object.values(this._events).every((events) => + events.every((event) => event.status === TaskStatus.COMPLETED) ) if (allTasksCompleted) { @@ -190,21 +201,21 @@ export class TerminalTaskTracker { addEvent(event: TaskEvent) { const { parent = 'root', taskId, name, status, inputs, output } = event - if (!this.events[parent]) { - this.events[parent] = [] + if (!this._events[parent]) { + this._events[parent] = [] } - const existingEventIndex = this.events[parent].findIndex( + const existingEventIndex = this._events[parent].findIndex( (e) => e.taskId === taskId ) if (existingEventIndex !== -1) { // If the event already exists, update its status and output: - this.events[parent][existingEventIndex].status = status - this.events[parent][existingEventIndex].output = output + this._events[parent][existingEventIndex].status = status + this._events[parent][existingEventIndex].output = output } else { // If the event does not exist, add it to the array: - this.events[parent].push({ taskId, name, status, inputs }) + this._events[parent].push({ taskId, name, status, inputs }) } } @@ -224,12 +235,12 @@ export class TerminalTaskTracker { } } - renderTree(node: string, level = 0) { + renderTree(node: string, level = 0): string[] { const indent = SPACE.repeat(level * 2) let lines: string[] = [] - if (this.events[node]) { - this.events[node].forEach(({ name, status, output, inputs }) => { + if (this._events[node]) { + this._events[node].forEach(({ name, status, output, inputs }) => { const [statusSymbol, color] = this.getStatusSymbolColor(status) lines.push( @@ -240,7 +251,7 @@ export class TerminalTaskTracker { gray('(' + this.stringify(inputs) + ')') ) - if (this.events[name]) { + if (this._events[name]) { lines = lines.concat( this.renderTree(name, level + 1).map((line, index, arr) => { if (index === arr.length - 1) { @@ -253,7 +264,7 @@ export class TerminalTaskTracker { } let line = '' - if (this.events[name]) { + if (this._events[name]) { line = indent + gray(SYMBOLS.BAR_END) } @@ -308,57 +319,61 @@ export class TerminalTaskTracker { process.stderr.write(MAGIC_STRING + output) } - toggleView(direction) { + toggleView(direction: string) { const viewModes = ['tasks', 'stdout', 'stderr'] - const currentIdx = viewModes.indexOf(this.viewMode) + const currentIdx = viewModes.indexOf(this._viewMode) if (direction === 'next') { - this.viewMode = viewModes[(currentIdx + 1) % viewModes.length] + this._viewMode = viewModes[(currentIdx + 1) % viewModes.length] } else if (direction === 'prev') { - this.viewMode = + this._viewMode = viewModes[(currentIdx - 1 + viewModes.length) % viewModes.length] } this.render() } - getSpinnerSymbol() { + getSpinnerSymbol(): string { return SYMBOLS.SPINNER[ Math.floor(Date.now() / this._spinnerInterval) % SYMBOLS.SPINNER.length ] } renderHeader() { - const legend = [ - 'commands', - 'ctrl+c exit', + const commands = [ + 'ctrl+c: exit', 'ctrl+e: truncate output', 'ctrl+left/right: switch view' ].join(' | ') - const header = [` Agentic: ${this.viewMode} `, ` ${legend} `, ''].join('\n') + const header = [ + ` Agentic - ${capitalize(this._viewMode)} View`, + ' ' + commands + ' ', + '', + '' + ].join('\n') this.writeWithMagicString(bgWhite(black(header))) } render() { - if (this.renderingPaused) { + if (this._renderingPaused) { return // Do not render if paused } this.clearAndSetCursorPosition() - if (this.viewMode === 'tasks') { + if (this._viewMode === 'tasks') { const lines = this.renderTree('root') - this.clearPreviousRender(lines.length + 2) + this.clearPreviousRender(lines.length) this.renderHeader() this.writeWithMagicString(lines) - } else if (this.viewMode === 'stdout') { - this.clearPreviousRender(this.stdoutBuffer.length + 2) + } else if (this._viewMode === 'stdout') { + this.clearPreviousRender(this._stdoutBuffer.length) this.renderHeader() - this.writeWithMagicString(this.stdoutBuffer) - } else if (this.viewMode === 'stderr') { - this.clearPreviousRender(this.stderrBuffer.length + 2) + this.writeWithMagicString(this._stdoutBuffer) + } else if (this._viewMode === 'stderr') { + this.clearPreviousRender(this._stderrBuffer.length) this.renderHeader() - this.writeWithMagicString(this.stderrBuffer) + this.writeWithMagicString(this._stderrBuffer) } } } diff --git a/legacy/packages/core/src/utils.ts b/legacy/packages/core/src/utils.ts index 64b92ef3..b58bec62 100644 --- a/legacy/packages/core/src/utils.ts +++ b/legacy/packages/core/src/utils.ts @@ -254,3 +254,7 @@ export function isArray(value: any): value is any[] { export function identity(x: T): T { return x } + +export function capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1) +} From 1273af0b06823af71b4ee12d3fe71cc682f7dec7 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 30 Jun 2023 14:33:11 -0400 Subject: [PATCH 13/20] feat: log warnings and errors --- legacy/packages/core/src/events/tracker.ts | 34 ++++++++++++---------- legacy/packages/core/src/task.ts | 12 ++++++++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index 683877f3..d3c4870c 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -228,7 +228,7 @@ export class TerminalTaskTracker { case TaskStatus.FAILED: return [SYMBOLS.CROSS, red] case TaskStatus.RETRYING: - return [SYMBOLS.WARNING, yellow] + return [this.getSpinnerSymbol(), yellow] case TaskStatus.RUNNING: default: return [this.getSpinnerSymbol(), cyan] @@ -268,20 +268,24 @@ export class TerminalTaskTracker { line = indent + gray(SYMBOLS.BAR_END) } - if (output) { - if (status === TaskStatus.COMPLETED) { - const formattedOutput = this.stringify(output) - line += - indent + - ' ' + - gray(SYMBOLS.RIGHT_ARROW + SPACE + formattedOutput) - } else if (status === TaskStatus.FAILED) { - line += - indent + ' ' + gray(SYMBOLS.RIGHT_ARROW + SPACE + red(output)) - } else if (status === TaskStatus.RETRYING) { - line += - indent + ' ' + gray(SYMBOLS.RIGHT_ARROW + SPACE + yellow(output)) - } + const formattedOutput = this.stringify(output || '') + if (status === TaskStatus.COMPLETED) { + line += + indent + ' ' + gray(SYMBOLS.RIGHT_ARROW + SPACE + formattedOutput) + } else if (status === TaskStatus.FAILED) { + line += + indent + + ' ' + + gray(SYMBOLS.RIGHT_ARROW) + + SPACE + + red(formattedOutput) + } else if (status === TaskStatus.RETRYING) { + line += + indent + + ' ' + + yellow(SYMBOLS.WARNING) + + SPACE + + gray(formattedOutput) } lines.push(line) diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index eb4ce053..c6188c33 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -360,6 +360,12 @@ export abstract class BaseTask< ctx.attemptNumber = err.attemptNumber + 1 ctx.metadata.error = err + this._eventEmitter.emit(TaskStatus.RETRYING, { + taskInputs: input, + taskOutput: err, + ...ctx.metadata + }) + if (err instanceof errors.ZodOutputValidationError) { ctx.retryMessage = err.message return @@ -383,6 +389,12 @@ export abstract class BaseTask< // task for now. return } else { + this._eventEmitter.emit(TaskStatus.FAILED, { + taskInputs: input, + taskOutput: err, + ...ctx.metadata + }) + throw err } } From 3bbce222099f636f03566fcc4ba08bcd67045257 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 30 Jun 2023 16:29:45 -0400 Subject: [PATCH 14/20] feat: use tree data structure --- legacy/packages/core/package.json | 1 + legacy/packages/core/src/events/tracker.ts | 151 ++++++++++++--------- legacy/pnpm-lock.yaml | 18 +++ 3 files changed, 108 insertions(+), 62 deletions(-) diff --git a/legacy/packages/core/package.json b/legacy/packages/core/package.json index a353efa7..08ad14d5 100644 --- a/legacy/packages/core/package.json +++ b/legacy/packages/core/package.json @@ -62,6 +62,7 @@ "p-timeout": "^6.1.2", "quick-lru": "^6.1.1", "replicate": "^0.12.3", + "tree-model": "^1.0.7", "ts-dedent": "^2.2.0", "type-fest": "^3.12.0", "zod": "^3.21.4", diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index d3c4870c..ba9ca38b 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -2,6 +2,7 @@ import process from 'node:process' import readline from 'node:readline' import { bgWhite, black, bold, cyan, gray, green, red, yellow } from 'colorette' +import TreeModel from 'tree-model' import { SPACE } from '@/constants' import { capitalize } from '@/utils' @@ -23,7 +24,8 @@ export interface TerminalTaskTrackerOptions { } export class TerminalTaskTracker { - protected _events: Record = { root: [] } + protected _tree = new TreeModel() + protected _root = this._tree.parse({ id: 'root' }) protected _interval: NodeJS.Timeout | null = null protected _inactivityTimeout: NodeJS.Timeout | null = null protected _truncateOutput = false @@ -39,7 +41,7 @@ export class TerminalTaskTracker { constructor({ spinnerInterval = 100, - inactivityThreshold = 2_000 + inactivityThreshold = 3_000 }: TerminalTaskTrackerOptions = {}) { this._spinnerInterval = spinnerInterval this._inactivityThreshold = inactivityThreshold @@ -187,9 +189,12 @@ export class TerminalTaskTracker { startInactivityTimeout() { this._inactivityTimeout = setTimeout(() => { - const allTasksCompleted = Object.values(this._events).every((events) => - events.every((event) => event.status === TaskStatus.COMPLETED) - ) + const allTasksCompleted = this._root.all((node) => { + return ( + node.model.status === TaskStatus.COMPLETED || + node.model.status === TaskStatus.FAILED + ) + }) if (allTasksCompleted) { this.close() @@ -200,22 +205,34 @@ export class TerminalTaskTracker { } addEvent(event: TaskEvent) { - const { parent = 'root', taskId, name, status, inputs, output } = event - if (!this._events[parent]) { - this._events[parent] = [] - } - - const existingEventIndex = this._events[parent].findIndex( - (e) => e.taskId === taskId + const { + parentTaskId = 'root', + taskId: id, + name, + status, + inputs, + output + } = event + const parentNode = this._root.first( + (node) => node.model.id === parentTaskId ) - if (existingEventIndex !== -1) { + const existingEventNode = parentNode + ? parentNode.first((node) => node.model.id === id) + : null + + if (existingEventNode) { // If the event already exists, update its status and output: - this._events[parent][existingEventIndex].status = status - this._events[parent][existingEventIndex].output = output + existingEventNode.model.status = status + existingEventNode.model.output = output } else { // If the event does not exist, add it to the array: - this._events[parent].push({ taskId, name, status, inputs }) + const node = this._tree.parse({ id, name, status, inputs, output: null }) + if (parentNode) { + parentNode.addChild(node) + } else { + this._root.addChild(node) + } } } @@ -235,61 +252,71 @@ export class TerminalTaskTracker { } } - renderTree(node: string, level = 0): string[] { + renderTree(id?: string, level = 0): string[] { const indent = SPACE.repeat(level * 2) let lines: string[] = [] - if (this._events[node]) { - this._events[node].forEach(({ name, status, output, inputs }) => { - const [statusSymbol, color] = this.getStatusSymbolColor(status) + const root = id + ? this._root.first((node) => node.model.id === id) + : this._root - lines.push( - indent + - color(statusSymbol) + - SPACE + - bold(name) + - gray('(' + this.stringify(inputs) + ')') - ) + if (root?.children) { + root.children.forEach( + ({ model: { id, name, status, output, inputs } }) => { + const [statusSymbol, color] = this.getStatusSymbolColor(status) - if (this._events[name]) { - lines = lines.concat( - this.renderTree(name, level + 1).map((line, index, arr) => { - if (index === arr.length - 1) { - return indent + gray(SYMBOLS.BAR) + line - } - - return indent + gray(SYMBOLS.BAR) + line - }) + lines.push( + indent + + color(statusSymbol) + + SPACE + + bold(name) + + gray('(' + this.stringify(inputs) + ')') ) - } - let line = '' - if (this._events[name]) { - line = indent + gray(SYMBOLS.BAR_END) - } + const hasChildren = root.hasChildren() - const formattedOutput = this.stringify(output || '') - if (status === TaskStatus.COMPLETED) { - line += - indent + ' ' + gray(SYMBOLS.RIGHT_ARROW + SPACE + formattedOutput) - } else if (status === TaskStatus.FAILED) { - line += - indent + - ' ' + - gray(SYMBOLS.RIGHT_ARROW) + - SPACE + - red(formattedOutput) - } else if (status === TaskStatus.RETRYING) { - line += - indent + - ' ' + - yellow(SYMBOLS.WARNING) + - SPACE + - gray(formattedOutput) - } + if (hasChildren) { + lines = lines.concat( + this.renderTree(id, level + 1).map((line, index, arr) => { + if (index === arr.length - 1) { + return indent + gray(SYMBOLS.BAR) + line + } - lines.push(line) - }) + return indent + gray(SYMBOLS.BAR) + line + }) + ) + } + + let line = '' + if (hasChildren) { + line = indent + gray(SYMBOLS.BAR_END) + } + + const formattedOutput = this.stringify(output || '') + if (status === TaskStatus.COMPLETED) { + line += + indent + + ' ' + + gray(SYMBOLS.RIGHT_ARROW + SPACE + formattedOutput) + } else if (status === TaskStatus.FAILED) { + line += + indent + + ' ' + + gray(SYMBOLS.RIGHT_ARROW) + + SPACE + + red(formattedOutput) + } else if (status === TaskStatus.RETRYING) { + line += + indent + + ' ' + + yellow(SYMBOLS.WARNING) + + SPACE + + gray(formattedOutput) + } + + lines.push(line) + } + ) } return lines diff --git a/legacy/pnpm-lock.yaml b/legacy/pnpm-lock.yaml index 8aec885e..644c420e 100644 --- a/legacy/pnpm-lock.yaml +++ b/legacy/pnpm-lock.yaml @@ -267,6 +267,9 @@ importers: replicate: specifier: ^0.12.3 version: 0.12.3 + tree-model: + specifier: ^1.0.7 + version: 1.0.7 ts-dedent: specifier: ^2.2.0 version: 2.2.0 @@ -3449,6 +3452,10 @@ packages: to-regex-range: 5.0.1 dev: true + /find-insert-index@0.0.1: + resolution: {integrity: sha512-eIqFuQzY7XwpAJ3sHWKFNGLx1nm3w/IhmFASETcx5sUuCaOUd3xDqRK/376SzXMVVJQaJUCPlS7L841T0xpFjQ==} + dev: false + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -4924,6 +4931,10 @@ packages: engines: {node: '>= 8'} dev: true + /mergesort@0.0.1: + resolution: {integrity: sha512-WKghTBzqAvTt9rG5TWS78Dmk2kCCL9VkkX8Zi9kKfJ4iqYpvcGGpeYtkhPHa9NZAPLivZiZsdO/LBG3ENayDmQ==} + dev: false + /mermaid@10.2.3: resolution: {integrity: sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw==} dependencies: @@ -7026,6 +7037,13 @@ packages: hasBin: true dev: true + /tree-model@1.0.7: + resolution: {integrity: sha512-oP4LUbCVtD2gcjcRaeI4L5hY60tHzB+AK/bthIJ2Pq1EUUOio5/xFzPWnGoBZlhtqpqbOkhFDzKIwKLOn0kccQ==} + dependencies: + find-insert-index: 0.0.1 + mergesort: 0.0.1 + dev: false + /trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} dev: false From 2178b7db2ff818af633d3ec758acd49da9536d10 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 30 Jun 2023 16:31:35 -0400 Subject: [PATCH 15/20] docs: add comments --- legacy/packages/core/src/events/emitters.ts | 3 ++ legacy/packages/core/src/events/event.ts | 31 +++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/legacy/packages/core/src/events/emitters.ts b/legacy/packages/core/src/events/emitters.ts index a05746a2..2b54ad6d 100644 --- a/legacy/packages/core/src/events/emitters.ts +++ b/legacy/packages/core/src/events/emitters.ts @@ -6,6 +6,9 @@ import { BaseTask } from '@/task' import { TaskEvent, TaskStatus } from './event' +/** + * Event emitter for task events. + */ export class TaskEventEmitter< TInput extends types.TaskInput = void, TOutput extends types.TaskOutput = string diff --git a/legacy/packages/core/src/events/event.ts b/legacy/packages/core/src/events/event.ts index 85d85365..b02bd568 100644 --- a/legacy/packages/core/src/events/event.ts +++ b/legacy/packages/core/src/events/event.ts @@ -11,10 +11,29 @@ export interface EventPayload { * Data required to create a new Event object. */ export interface EventData { + /** + * Event identifier + */ id?: string + + /** + * Event timestamp + */ timestamp?: Date + + /** + * Key-value pairs holding event data. + */ payload?: T + + /** + * Version of the event. + */ version?: number + + /** + * Event type. + */ type?: string } @@ -57,6 +76,9 @@ export class Event { return new Type(data) } + /** + * Serializes an event into a JSON string. + */ toJSON(): string { return JSON.stringify({ id: this.id, @@ -67,6 +89,9 @@ export class Event { }) } + /** + * Returns a human-readable string representation of an event. + */ toString(): string { return `Event { id: ${ this.id @@ -85,7 +110,7 @@ export interface TaskEventPayload extends EventPayload { taskStatus: TaskStatus taskInputs?: TInput taskOutput?: TOutput - taskParent?: string + parentTaskId?: string } /** @@ -126,7 +151,7 @@ export class TaskEvent extends Event< return this.payload?.taskOutput ?? '' } - get parent(): string { - return this.payload?.taskParent ?? 'root' + get parentTaskId(): string { + return this.payload?.parentTaskId ?? 'root' } } From 02f73e9455e25669865ebe89d1c1267922f42ea9 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 30 Jun 2023 16:50:29 -0400 Subject: [PATCH 16/20] fix: resolve unfinished check bug and add pending status --- legacy/packages/core/src/events/event.ts | 1 + legacy/packages/core/src/events/tracker.ts | 27 ++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/legacy/packages/core/src/events/event.ts b/legacy/packages/core/src/events/event.ts index b02bd568..37a3eeb5 100644 --- a/legacy/packages/core/src/events/event.ts +++ b/legacy/packages/core/src/events/event.ts @@ -119,6 +119,7 @@ export interface TaskEventPayload extends EventPayload { export enum TaskStatus { COMPLETED = 'COMPLETED', FAILED = 'FAILED', + PENDING = 'PENDING', RETRYING = 'RETRYING', SKIPPED = 'SKIPPED', RUNNING = 'RUNNING', diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index ba9ca38b..fe7a136f 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -12,6 +12,8 @@ import { SYMBOLS } from './symbols' export const MAGIC_STRING = '__INSIDE_TRACKER__' // a unique "magic" string that used to identify the output of the tracker +const TWO_SPACES = `${SPACE}${SPACE}` + // eslint-disable-next-line no-control-regex const RE_ANSI_ESCAPES = /^(\x1b\[[0-9;]*[ABCDHJK]|[\r\n])+$/ // cursor movement, screen clearing, etc. @@ -20,7 +22,7 @@ const originalStderrWrite = process.stderr.write export interface TerminalTaskTrackerOptions { spinnerInterval?: number - inactivityThreshold?: number + inactivityInterval?: number } export class TerminalTaskTracker { @@ -34,17 +36,17 @@ export class TerminalTaskTracker { protected _renderingPaused = false protected _spinnerInterval: number - protected _inactivityThreshold: number + protected _inactivityInterval: number private _stdoutBuffer: string[] = [] private _stderrBuffer: string[] = [] constructor({ spinnerInterval = 100, - inactivityThreshold = 3_000 + inactivityInterval = 2_000 }: TerminalTaskTrackerOptions = {}) { this._spinnerInterval = spinnerInterval - this._inactivityThreshold = inactivityThreshold + this._inactivityInterval = inactivityInterval if (!process.stderr.isTTY) { // If stderr is not a TTY, don't render any dynamic output... @@ -189,19 +191,20 @@ export class TerminalTaskTracker { startInactivityTimeout() { this._inactivityTimeout = setTimeout(() => { - const allTasksCompleted = this._root.all((node) => { + const unfinishedTasks = this._root.all((node) => { return ( - node.model.status === TaskStatus.COMPLETED || - node.model.status === TaskStatus.FAILED + node.model.status == TaskStatus.RUNNING || + node.model.status == TaskStatus.RETRYING || + node.model.status == TaskStatus.PENDING ) }) - if (allTasksCompleted) { + if (unfinishedTasks.length === 0) { this.close() } else { this.startInactivityTimeout() } - }, this._inactivityThreshold) + }, this._inactivityInterval) } addEvent(event: TaskEvent) { @@ -222,11 +225,9 @@ export class TerminalTaskTracker { : null if (existingEventNode) { - // If the event already exists, update its status and output: existingEventNode.model.status = status existingEventNode.model.output = output } else { - // If the event does not exist, add it to the array: const node = this._tree.parse({ id, name, status, inputs, output: null }) if (parentNode) { parentNode.addChild(node) @@ -373,7 +374,9 @@ export class TerminalTaskTracker { renderHeader() { const commands = [ 'ctrl+c: exit', - 'ctrl+e: truncate output', + `ctrl+e: ${ + this._truncateOutput ? TWO_SPACES + 'expand' : 'truncate' + } output`, 'ctrl+left/right: switch view' ].join(' | ') From 999cb4a0fc7f8213a9b823367f67010e04c2d6f7 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 5 Jul 2023 13:56:10 -0400 Subject: [PATCH 17/20] Revert "chore: add eventemitter3 and remove unused deps" This reverts commit 8971ff64775f7e77ed68853e1fb2f4c425cbc0be. --- legacy/package.json | 38 +---- legacy/pnpm-lock.yaml | 313 ++++++++++++++---------------------------- 2 files changed, 103 insertions(+), 248 deletions(-) diff --git a/legacy/package.json b/legacy/package.json index 1529f751..94115b60 100644 --- a/legacy/package.json +++ b/legacy/package.json @@ -23,46 +23,12 @@ "release": "turbo clean && turbo build && changeset publish", "test": "turbo test", "pre-commit": "lint-staged", - "test:prettier": "prettier \"**/*.{js,jsx,ts,tsx}\" --check", - "test:eslint": "eslint \"**/*.ts\"", - "test-cov": "c8 ava" - }, - "dependencies": { - "@agentic/midjourney-fetch": "^1.0.1", - "@anthropic-ai/sdk": "^0.4.4", - "@inquirer/checkbox": "^1.3.2", - "@inquirer/editor": "^1.2.1", - "@inquirer/input": "^1.2.2", - "@inquirer/select": "^1.2.2", - "colorette": "^2.0.20", - "debug": "^4.3.4", - "eventemitter3": "^5.0.1", - "expr-eval": "^2.0.2", - "handlebars": "^4.7.7", - "is-relative-url": "^4.0.0", - "is-unicode-supported": "^1.3.0", - "js-tiktoken": "^1.0.7", - "jsonrepair": "^3.2.0", - "ky": "^0.33.3", - "nanoid": "^4.0.2", - "normalize-url": "^8.0.0", - "openai-fetch": "^1.6.3", - "p-map": "^6.0.0", - "p-retry": "^5.1.2", - "p-throttle": "^5.1.0", - "p-timeout": "^6.1.2", - "quick-lru": "^6.1.1", - "replicate": "^0.12.3", - "ts-dedent": "^2.2.0", - "zod": "^3.21.4", - "zod-to-json-schema": "^3.21.2", - "zod-validation-error": "^1.3.1" + "version-packages": "changeset version" }, "devDependencies": { "@changesets/cli": "^2.26.2", "@trivago/prettier-plugin-sort-imports": "^4.1.1", "@types/node": "^20.3.2", - "@types/sinon": "^10.0.15", "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "ava": "^5.3.1", @@ -105,4 +71,4 @@ "guardrails", "plugins" ] -} \ No newline at end of file +} diff --git a/legacy/pnpm-lock.yaml b/legacy/pnpm-lock.yaml index 644c420e..fb587442 100644 --- a/legacy/pnpm-lock.yaml +++ b/legacy/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -7,94 +7,6 @@ settings: importers: .: - dependencies: - '@agentic/midjourney-fetch': - specifier: ^1.0.1 - version: 1.0.1 - '@anthropic-ai/sdk': - specifier: ^0.4.4 - version: 0.4.4 - '@inquirer/checkbox': - specifier: ^1.3.2 - version: 1.3.2 - '@inquirer/editor': - specifier: ^1.2.1 - version: 1.2.1 - '@inquirer/input': - specifier: ^1.2.2 - version: 1.2.2 - '@inquirer/select': - specifier: ^1.2.2 - version: 1.2.2 - colorette: - specifier: ^2.0.20 - version: 2.0.20 - debug: - specifier: ^4.3.4 - version: 4.3.4 - eventemitter3: - specifier: ^5.0.1 - version: 5.0.1 - expr-eval: - specifier: ^2.0.2 - version: 2.0.2 - handlebars: - specifier: ^4.7.7 - version: 4.7.7 - is-relative-url: - specifier: ^4.0.0 - version: 4.0.0 - is-unicode-supported: - specifier: ^1.3.0 - version: 1.3.0 - js-tiktoken: - specifier: ^1.0.7 - version: 1.0.7 - jsonrepair: - specifier: ^3.2.0 - version: 3.2.0 - ky: - specifier: ^0.33.3 - version: 0.33.3 - nanoid: - specifier: ^4.0.2 - version: 4.0.2 - normalize-url: - specifier: ^8.0.0 - version: 8.0.0 - openai-fetch: - specifier: ^1.6.3 - version: 1.6.3 - p-map: - specifier: ^6.0.0 - version: 6.0.0 - p-retry: - specifier: ^5.1.2 - version: 5.1.2 - p-throttle: - specifier: ^5.1.0 - version: 5.1.0 - p-timeout: - specifier: ^6.1.2 - version: 6.1.2 - quick-lru: - specifier: ^6.1.1 - version: 6.1.1 - replicate: - specifier: ^0.12.3 - version: 0.12.3 - ts-dedent: - specifier: ^2.2.0 - version: 2.2.0 - zod: - specifier: ^3.21.4 - version: 3.21.4 - zod-to-json-schema: - specifier: ^3.21.2 - version: 3.21.2(zod@3.21.4) - zod-validation-error: - specifier: ^1.3.1 - version: 1.3.1(zod@3.21.4) devDependencies: '@changesets/cli': specifier: ^2.26.2 @@ -105,9 +17,6 @@ importers: '@types/node': specifier: ^20.3.2 version: 20.3.2 - '@types/sinon': - specifier: ^10.0.15 - version: 10.0.15 '@typescript-eslint/eslint-plugin': specifier: ^5.60.1 version: 5.60.1(@typescript-eslint/parser@5.60.1)(eslint@8.43.0)(typescript@5.1.6) @@ -310,6 +219,11 @@ importers: packages: + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + /@agentic/midjourney-fetch@1.0.1: resolution: {integrity: sha512-CUGNh3YJfbAPrOKcmqOOWuWQrkgVY+pWbCGhZkq74Jlc6oP8Jb+/K61V7Jhf/liYgYopbrpoDDue+ZKqijW/2A==} engines: {node: '>=18', pnpm: '>=8'} @@ -639,7 +553,7 @@ packages: resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} dependencies: '@esbuild-kit/core-utils': 3.1.0 - get-tsconfig: 4.6.0 + get-tsconfig: 4.6.2 dev: true /@esbuild-kit/core-utils@3.1.0: @@ -653,7 +567,7 @@ packages: resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==} dependencies: '@esbuild-kit/core-utils': 3.1.0 - get-tsconfig: 4.6.0 + get-tsconfig: 4.6.2 dev: true /@esbuild/android-arm64@0.17.19: @@ -665,8 +579,8 @@ packages: dev: true optional: true - /@esbuild/android-arm64@0.18.4: - resolution: {integrity: sha512-yQVgO+V307hA2XhzELQ6F91CBGX7gSnlVGAj5YIqjQOxThDpM7fOcHT2YLJbE6gNdPtgRSafQrsK8rJ9xHCaZg==} + /@esbuild/android-arm64@0.18.10: + resolution: {integrity: sha512-ynm4naLbNbK0ajf9LUWtQB+6Vfg1Z/AplArqr4tGebC00Z6m9Y91OVIcjDa461wGcZwcaHYaZAab4yJxfhisTQ==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -683,8 +597,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.18.4: - resolution: {integrity: sha512-yKmQC9IiuvHdsNEbPHSprnMHg6OhL1cSeQZLzPpgzJBJ9ppEg9GAZN8MKj1TcmB4tZZUrq5xjK7KCmhwZP8iDA==} + /@esbuild/android-arm@0.18.10: + resolution: {integrity: sha512-3KClmVNd+Fku82uZJz5C4Rx8m1PPmWUFz5Zkw8jkpZPOmsq+EG1TTOtw1OXkHuX3WczOFQigrtf60B1ijKwNsg==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -701,8 +615,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.18.4: - resolution: {integrity: sha512-yLKXMxQg6sk1ntftxQ5uwyVgG4/S2E7UoOCc5N4YZW7fdkfRiYEXqm7CMuIfY2Vs3FTrNyKmSfNevIuIvJnMww==} + /@esbuild/android-x64@0.18.10: + resolution: {integrity: sha512-vFfXj8P9Yfjh54yqUDEHKzqzYuEfPyAOl3z7R9hjkwt+NCvbn9VMxX+IILnAfdImRBfYVItgSUsqGKhJFnBwZw==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -719,8 +633,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.18.4: - resolution: {integrity: sha512-MVPEoZjZpk2xQ1zckZrb8eQuQib+QCzdmMs3YZAYEQPg+Rztk5pUxGyk8htZOC8Z38NMM29W+MqY9Sqo/sDGKw==} + /@esbuild/darwin-arm64@0.18.10: + resolution: {integrity: sha512-k2OJQ7ZxE6sVc91+MQeZH9gFeDAH2uIYALPAwTjTCvcPy9Dzrf7V7gFUQPYkn09zloWhQ+nvxWHia2x2ZLR0sQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -737,8 +651,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.18.4: - resolution: {integrity: sha512-uEsRtYRUDsz7i2tXg/t/SyF+5gU1cvi9B6B8i5ebJgtUUHJYWyIPIesmIOL4/+bywjxsDMA/XrNFMgMffLnh5A==} + /@esbuild/darwin-x64@0.18.10: + resolution: {integrity: sha512-tnz/mdZk1L1Z3WpGjin/L2bKTe8/AKZpI8fcCLtH+gq8WXWsCNJSxlesAObV4qbtTl6pG5vmqFXfWUQ5hV8PAQ==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -755,8 +669,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.18.4: - resolution: {integrity: sha512-I8EOigqWnOHRin6Zp5Y1cfH3oT54bd7Sdz/VnpUNksbOtfp8IWRTH4pgkgO5jWaRQPjCpJcOpdRjYAMjPt8wXg==} + /@esbuild/freebsd-arm64@0.18.10: + resolution: {integrity: sha512-QJluV0LwBrbHnYYwSKC+K8RGz0g/EyhpQH1IxdoFT0nM7PfgjE+aS8wxq/KFEsU0JkL7U/EEKd3O8xVBxXb2aA==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -773,8 +687,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.18.4: - resolution: {integrity: sha512-1bHfgMz/cNMjbpsYxjVgMJ1iwKq+NdDPlACBrWULD7ZdFmBQrhMicMaKb5CdmdVyvIwXmasOuF4r6Iq574kUTA==} + /@esbuild/freebsd-x64@0.18.10: + resolution: {integrity: sha512-Hi/ycUkS6KTw+U9G5PK5NoK7CZboicaKUSVs0FSiPNtuCTzK6HNM4DIgniH7hFaeuszDS9T4dhAHWiLSt/Y5Ng==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -791,8 +705,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.18.4: - resolution: {integrity: sha512-J42vLHaYREyiBwH0eQE4/7H1DTfZx8FuxyWSictx4d7ezzuKE3XOkIvOg+SQzRz7T9HLVKzq2tvbAov4UfufBw==} + /@esbuild/linux-arm64@0.18.10: + resolution: {integrity: sha512-Nz6XcfRBOO7jSrVpKAyEyFOPGhySPNlgumSDhWAspdQQ11ub/7/NZDMhWDFReE9QH/SsCOCLQbdj0atAk/HMOQ==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -809,8 +723,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.18.4: - resolution: {integrity: sha512-4XCGqM/Ay1LCXUBH59bL4JbSbbTK1K22dWHymWMGaEh2sQCDOUw+OQxozYV/YdBb91leK2NbuSrE2BRamwgaYw==} + /@esbuild/linux-arm@0.18.10: + resolution: {integrity: sha512-HfFoxY172tVHPIvJy+FHxzB4l8xU7e5cxmNS11cQ2jt4JWAukn/7LXaPdZid41UyTweqa4P/1zs201gRGCTwHw==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -827,8 +741,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.18.4: - resolution: {integrity: sha512-4ksIqFwhq7OExty7Sl1n0vqQSCqTG4sU6i99G2yuMr28CEOUZ/60N+IO9hwI8sIxBqmKmDgncE1n5CMu/3m0IA==} + /@esbuild/linux-ia32@0.18.10: + resolution: {integrity: sha512-otMdmSmkMe+pmiP/bZBjfphyAsTsngyT9RCYwoFzqrveAbux9nYitDTpdgToG0Z0U55+PnH654gCH2GQ1aB6Yw==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -845,8 +759,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.18.4: - resolution: {integrity: sha512-bsWtoVHkGQgAsFXioDueXRiUIfSGrVkJjBBz4gcBJxXcD461cWFQFyu8Fxdj9TP+zEeqJ8C/O4LFFMBNi6Fscw==} + /@esbuild/linux-loong64@0.18.10: + resolution: {integrity: sha512-t8tjFuON1koxskzQ4VFoh0T5UDUMiLYjwf9Wktd0tx8AoK6xgU+5ubKOpWpcnhEQ2tESS5u0v6QuN8PX/ftwcQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -863,8 +777,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.18.4: - resolution: {integrity: sha512-LRD9Fu8wJQgIOOV1o3nRyzrheFYjxA0C1IVWZ93eNRRWBKgarYFejd5WBtrp43cE4y4D4t3qWWyklm73Mrsd/g==} + /@esbuild/linux-mips64el@0.18.10: + resolution: {integrity: sha512-+dUkcVzcfEJHz3HEnVpIJu8z8Wdn2n/nWMWdl6FVPFGJAVySO4g3+XPzNKFytVFwf8hPVDwYXzVcu8GMFqsqZw==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -881,8 +795,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.18.4: - resolution: {integrity: sha512-jtQgoZjM92gauVRxNaaG/TpL3Pr4WcL3Pwqi9QgdrBGrEXzB+twohQiWNSTycs6lUygakos4mm2h0B9/SHveng==} + /@esbuild/linux-ppc64@0.18.10: + resolution: {integrity: sha512-sO3PjjxEGy+PY2qkGe2gwJbXdZN9wAYpVBZWFD0AwAoKuXRkWK0/zaMQ5ekUFJDRDCRm8x5U0Axaub7ynH/wVg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -899,8 +813,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.18.4: - resolution: {integrity: sha512-7WaU/kRZG0VCV09Xdlkg6LNAsfU9SAxo6XEdaZ8ffO4lh+DZoAhGTx7+vTMOXKxa+r2w1LYDGxfJa2rcgagMRA==} + /@esbuild/linux-riscv64@0.18.10: + resolution: {integrity: sha512-JDtdbJg3yjDeXLv4lZYE1kiTnxv73/8cbPHY9T/dUKi8rYOM/k5b3W4UJLMUksuQ6nTm5c89W1nADsql6FW75A==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -917,8 +831,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.18.4: - resolution: {integrity: sha512-D19ed0xreKQvC5t+ArE2njSnm18WPpE+1fhwaiJHf+Xwqsq+/SUaV8Mx0M27nszdU+Atq1HahrgCOZCNNEASUg==} + /@esbuild/linux-s390x@0.18.10: + resolution: {integrity: sha512-NLuSKcp8WckjD2a7z5kzLiCywFwBTMlIxDNuud1AUGVuwBBJSkuubp6cNjJ0p5c6CZaA3QqUGwjHJBiG1SoOFw==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -935,8 +849,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.18.4: - resolution: {integrity: sha512-Rx3AY1sxyiO/gvCGP00nL69L60dfmWyjKWY06ugpB8Ydpdsfi3BHW58HWC24K3CAjAPSwxcajozC2PzA9JBS1g==} + /@esbuild/linux-x64@0.18.10: + resolution: {integrity: sha512-wj2KRsCsFusli+6yFgNO/zmmLslislAWryJnodteRmGej7ZzinIbMdsyp13rVGde88zxJd5vercNYK9kuvlZaQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -953,8 +867,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.18.4: - resolution: {integrity: sha512-AaShPmN9c6w1mKRpliKFlaWcSkpBT4KOlk93UfFgeI3F3cbjzdDKGsbKnOZozmYbE1izZKLmNJiW0sFM+A5JPA==} + /@esbuild/netbsd-x64@0.18.10: + resolution: {integrity: sha512-pQ9QqxEPI3cVRZyUtCoZxhZK3If+7RzR8L2yz2+TDzdygofIPOJFaAPkEJ5rYIbUO101RaiYxfdOBahYexLk5A==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -971,8 +885,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.18.4: - resolution: {integrity: sha512-tRGvGwou3BrvHVvF8HxTqEiC5VtPzySudS9fh2jBIKpLX7HCW8jIkW+LunkFDNwhslx4xMAgh0jAHsx/iCymaQ==} + /@esbuild/openbsd-x64@0.18.10: + resolution: {integrity: sha512-k8GTIIW9I8pEEfoOUm32TpPMgSg06JhL5DO+ql66aLTkOQUs0TxCA67Wi7pv6z8iF8STCGcNbm3UWFHLuci+ag==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -989,8 +903,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.18.4: - resolution: {integrity: sha512-acORFDI95GKhmAnlH8EarBeuqoy/j3yxIU+FDB91H3+ZON+8HhTadtT450YkaMzX6lEWbhi+mjVUCj00M5yyOQ==} + /@esbuild/sunos-x64@0.18.10: + resolution: {integrity: sha512-vIGYJIdEI6d4JBucAx8py792G8J0GP40qSH+EvSt80A4zvGd6jph+5t1g+eEXcS2aRpgZw6CrssNCFZxTdEsxw==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -1007,8 +921,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.18.4: - resolution: {integrity: sha512-1NxP+iOk8KSvS1L9SSxEvBAJk39U0GiGZkiiJGbuDF9G4fG7DSDw6XLxZMecAgmvQrwwx7yVKdNN3GgNh0UfKg==} + /@esbuild/win32-arm64@0.18.10: + resolution: {integrity: sha512-kRhNcMZFGMW+ZHCarAM1ypr8OZs0k688ViUCetVCef9p3enFxzWeBg9h/575Y0nsFu0ZItluCVF5gMR2pwOEpA==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -1025,8 +939,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.18.4: - resolution: {integrity: sha512-OKr8jze93vbgqZ/r23woWciTixUwLa976C9W7yNBujtnVHyvsL/ocYG61tsktUfJOpyIz5TsohkBZ6Lo2+PCcQ==} + /@esbuild/win32-ia32@0.18.10: + resolution: {integrity: sha512-AR9PX1whYaYh9p0EOaKna0h48F/A101Mt/ag72+kMkkBZXPQ7cjbz2syXI/HI3OlBdUytSdHneljfjvUoqwqiQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -1043,8 +957,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.18.4: - resolution: {integrity: sha512-qJr3wVvcLjPFcV4AMDS3iquhBfTef2zo/jlm8RMxmiRp3Vy2HY8WMxrykJlcbCnqLXZPA0YZxZGND6eug85ogg==} + /@esbuild/win32-x64@0.18.10: + resolution: {integrity: sha512-5sTkYhAGHNRr6bVf4RM0PsscqVr6/DBYdrlMh168oph3usid3lKHcHEEHmr34iZ9GHeeg2juFOxtpl6XyC3tpw==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1567,7 +1481,7 @@ packages: /@swc/helpers@0.5.1: resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: - tslib: 2.5.3 + tslib: 2.6.0 dev: false /@theguild/remark-mermaid@0.0.3(react@18.2.0): @@ -1738,7 +1652,7 @@ packages: grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 - semver: 7.5.2 + semver: 7.5.3 tsutils: 3.21.0(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: @@ -2040,7 +1954,7 @@ packages: debug: 4.3.4 emittery: 1.0.1 figures: 5.0.0 - globby: 13.1.4 + globby: 13.2.0 ignore-by-default: 2.1.0 indent-string: 5.0.0 is-error: 2.2.2 @@ -2129,13 +2043,13 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true - /bundle-require@4.0.1(esbuild@0.18.4): + /bundle-require@4.0.1(esbuild@0.18.10): resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.17' dependencies: - esbuild: 0.18.4 + esbuild: 0.18.10 load-tsconfig: 0.2.5 dev: true @@ -2903,7 +2817,7 @@ packages: resolution: {integrity: sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==} engines: {node: '>=14.16'} dependencies: - globby: 13.1.4 + globby: 13.2.0 graceful-fs: 4.2.11 is-glob: 4.0.3 is-path-cwd: 3.0.0 @@ -3093,34 +3007,34 @@ packages: '@esbuild/win32-x64': 0.17.19 dev: true - /esbuild@0.18.4: - resolution: {integrity: sha512-9rxWV/Cb2DMUXfe9aUsYtqg0KTlw146ElFH22kYeK9KVV1qT082X4lpmiKsa12ePiCcIcB686TQJxaGAa9TFvA==} + /esbuild@0.18.10: + resolution: {integrity: sha512-33WKo67auOXzZHBY/9DTJRo7kIvfU12S+D4sp2wIz39N88MDIaCGyCwbW01RR70pK6Iya0I74lHEpyLfFqOHPA==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.18.4 - '@esbuild/android-arm64': 0.18.4 - '@esbuild/android-x64': 0.18.4 - '@esbuild/darwin-arm64': 0.18.4 - '@esbuild/darwin-x64': 0.18.4 - '@esbuild/freebsd-arm64': 0.18.4 - '@esbuild/freebsd-x64': 0.18.4 - '@esbuild/linux-arm': 0.18.4 - '@esbuild/linux-arm64': 0.18.4 - '@esbuild/linux-ia32': 0.18.4 - '@esbuild/linux-loong64': 0.18.4 - '@esbuild/linux-mips64el': 0.18.4 - '@esbuild/linux-ppc64': 0.18.4 - '@esbuild/linux-riscv64': 0.18.4 - '@esbuild/linux-s390x': 0.18.4 - '@esbuild/linux-x64': 0.18.4 - '@esbuild/netbsd-x64': 0.18.4 - '@esbuild/openbsd-x64': 0.18.4 - '@esbuild/sunos-x64': 0.18.4 - '@esbuild/win32-arm64': 0.18.4 - '@esbuild/win32-ia32': 0.18.4 - '@esbuild/win32-x64': 0.18.4 + '@esbuild/android-arm': 0.18.10 + '@esbuild/android-arm64': 0.18.10 + '@esbuild/android-x64': 0.18.10 + '@esbuild/darwin-arm64': 0.18.10 + '@esbuild/darwin-x64': 0.18.10 + '@esbuild/freebsd-arm64': 0.18.10 + '@esbuild/freebsd-x64': 0.18.10 + '@esbuild/linux-arm': 0.18.10 + '@esbuild/linux-arm64': 0.18.10 + '@esbuild/linux-ia32': 0.18.10 + '@esbuild/linux-loong64': 0.18.10 + '@esbuild/linux-mips64el': 0.18.10 + '@esbuild/linux-ppc64': 0.18.10 + '@esbuild/linux-riscv64': 0.18.10 + '@esbuild/linux-s390x': 0.18.10 + '@esbuild/linux-x64': 0.18.10 + '@esbuild/netbsd-x64': 0.18.10 + '@esbuild/openbsd-x64': 0.18.10 + '@esbuild/sunos-x64': 0.18.10 + '@esbuild/win32-arm64': 0.18.10 + '@esbuild/win32-ia32': 0.18.10 + '@esbuild/win32-x64': 0.18.10 dev: true /escalade@3.1.1: @@ -3214,7 +3128,7 @@ packages: lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.1 + optionator: 0.9.3 strip-ansi: 6.0.1 strip-json-comments: 3.1.1 text-table: 0.2.0 @@ -3311,10 +3225,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - dev: false - /execa@0.8.0: resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==} engines: {node: '>=4'} @@ -3593,8 +3503,8 @@ packages: get-intrinsic: 1.2.1 dev: true - /get-tsconfig@4.6.0: - resolution: {integrity: sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==} + /get-tsconfig@4.6.2: + resolution: {integrity: sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==} dependencies: resolve-pkg-maps: 1.0.0 dev: true @@ -3698,8 +3608,8 @@ packages: slash: 3.0.0 dev: true - /globby@13.1.4: - resolution: {integrity: sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==} + /globby@13.2.0: + resolution: {integrity: sha512-jWsQfayf13NvqKUIL3Ta+CIqMnvlaIDFveWE/dpOZ9+3AMEJozsxDvKA02zync9UuvOM8rOXzsD5GqKP4OnWPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: dir-glob: 3.0.1 @@ -5736,16 +5646,16 @@ packages: zod: 3.21.4 dev: false - /optionator@0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - word-wrap: 1.2.3 dev: true /os-tmpdir@1.0.2: @@ -6035,8 +5945,8 @@ packages: engines: {node: '>=6'} dev: true - /pirates@4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} dev: true @@ -6453,8 +6363,8 @@ packages: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} dev: false - /rollup@3.25.1: - resolution: {integrity: sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==} + /rollup@3.25.3: + resolution: {integrity: sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -6479,7 +6389,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.5.3 + tslib: 2.6.0 dev: true /sade@1.8.1: @@ -6524,14 +6434,6 @@ packages: hasBin: true dev: true - /semver@7.5.2: - resolution: {integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /semver@7.5.3: resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} engines: {node: '>=10'} @@ -6914,7 +6816,7 @@ packages: glob: 7.1.6 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.5 + pirates: 4.0.6 ts-interface-checker: 0.1.13 dev: true @@ -7075,8 +6977,8 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib@2.5.3: - resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + /tslib@2.6.0: + resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} /tsup@7.1.0(typescript@5.1.6): resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==} @@ -7094,17 +6996,17 @@ packages: typescript: optional: true dependencies: - bundle-require: 4.0.1(esbuild@0.18.4) + bundle-require: 4.0.1(esbuild@0.18.10) cac: 6.7.14 chokidar: 3.5.3 debug: 4.3.4 - esbuild: 0.18.4 + esbuild: 0.18.10 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 postcss-load-config: 4.0.1 resolve-from: 5.0.0 - rollup: 3.25.1 + rollup: 3.25.3 source-map: 0.8.0-beta.0 sucrase: 3.32.0 tree-kill: 1.2.2 @@ -7571,11 +7473,6 @@ packages: isexe: 2.0.0 dev: true - /word-wrap@1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} - engines: {node: '>=0.10.0'} - dev: true - /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: false @@ -7687,14 +7584,6 @@ packages: engines: {node: '>=12.20'} dev: true - /zod-to-json-schema@3.21.2(zod@3.21.4): - resolution: {integrity: sha512-02yfKymfmIf2rM/5LYGlyw0daEel/f3MsSGMNJZWWf44ato+Y+diFugOpDtgvEUn3cYM5oDAGWW2NHeSD4mByw==} - peerDependencies: - zod: ^3.21.4 - dependencies: - zod: 3.21.4 - dev: false - /zod-to-json-schema@3.21.3(zod@3.21.4): resolution: {integrity: sha512-09W/9oyxeF1/wWnzCb6MursW+lOzgKi91QwE7eTBbC+t/qgfuLsUVDai3lHemSQnQu/UONAcT/fv3ZnDvbTeKg==} peerDependencies: From eef5f88c29a1c8ffda14539b8b22a0a64cbcd688 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 5 Jul 2023 13:57:45 -0400 Subject: [PATCH 18/20] refactor: extend event emitter in agentic and task class --- legacy/packages/core/src/agentic.ts | 10 ++-- legacy/packages/core/src/events/emitters.ts | 54 --------------------- legacy/packages/core/src/events/index.ts | 1 - legacy/packages/core/src/task.ts | 54 +++++++++++++++------ 4 files changed, 41 insertions(+), 78 deletions(-) delete mode 100644 legacy/packages/core/src/events/emitters.ts diff --git a/legacy/packages/core/src/agentic.ts b/legacy/packages/core/src/agentic.ts index 110e378c..e543eb57 100644 --- a/legacy/packages/core/src/agentic.ts +++ b/legacy/packages/core/src/agentic.ts @@ -11,11 +11,10 @@ import { OpenAIChatCompletion } from './llms/openai' import { defaultLogger } from './logger' import { defaultIDGeneratorFn, isFunction, isString } from './utils' -export class Agentic { +export class Agentic extends EventEmitter { protected _ky: types.KyInstance protected _logger: types.Logger protected _taskTracker: TerminalTaskTracker - protected _eventEmitter: EventEmitter protected _openai?: types.openai.OpenAIClient protected _anthropic?: types.anthropic.Client @@ -41,6 +40,8 @@ export class Agentic { ky?: types.KyInstance taskTracker?: TerminalTaskTracker }) { + super() + // TODO: This is a bit hacky, but we're doing it to have a slightly nicer API // for the end developer when creating subclasses of `BaseTask` to use as // tools. @@ -54,7 +55,6 @@ export class Agentic { this._ky = opts.ky ?? defaultKy this._logger = opts.logger ?? defaultLogger this._taskTracker = opts.taskTracker ?? new TerminalTaskTracker() - this._eventEmitter = new EventEmitter() this._openaiModelDefaults = { provider: 'openai', @@ -109,10 +109,6 @@ export class Agentic { return this._taskTracker } - public get eventEmitter(): EventEmitter { - return this._eventEmitter - } - public get idGeneratorFn(): types.IDGeneratorFunction { return this._idGeneratorFn } diff --git a/legacy/packages/core/src/events/emitters.ts b/legacy/packages/core/src/events/emitters.ts deleted file mode 100644 index 2b54ad6d..00000000 --- a/legacy/packages/core/src/events/emitters.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { EventEmitter } from 'eventemitter3' - -import * as types from '@/types' -import type { Agentic } from '@/agentic' -import { BaseTask } from '@/task' - -import { TaskEvent, TaskStatus } from './event' - -/** - * Event emitter for task events. - */ -export class TaskEventEmitter< - TInput extends types.TaskInput = void, - TOutput extends types.TaskOutput = string -> extends EventEmitter { - protected _agentic: Agentic - protected _task: BaseTask - - constructor(task: BaseTask, agentic: Agentic) { - super() - - this._task = task - this._agentic = agentic - } - - on( - takStatus: T, - fn: (event: TaskEvent) => void, - context?: any - ): this { - return super.on(takStatus, fn, context) - } - - emit(taskStatus: string | symbol, payload: object = {}): boolean { - if (!Object.values(TaskStatus).includes(taskStatus as TaskStatus)) { - throw new Error(`Invalid task status: ${String(taskStatus)}`) - } - - const { id, nameForModel } = this._task - const event = new TaskEvent({ - payload: { - taskStatus: taskStatus as TaskStatus, - taskId: id, - taskName: nameForModel, - ...payload - } - }) - this._agentic.taskTracker.addEvent(event) - - this._agentic.eventEmitter.emit(taskStatus, event) - - return super.emit(taskStatus, event) - } -} diff --git a/legacy/packages/core/src/events/index.ts b/legacy/packages/core/src/events/index.ts index c52faae3..b15f5f24 100644 --- a/legacy/packages/core/src/events/index.ts +++ b/legacy/packages/core/src/events/index.ts @@ -1,3 +1,2 @@ -export * from './emitters' export * from './event' export * from './tracker' diff --git a/legacy/packages/core/src/task.ts b/legacy/packages/core/src/task.ts index c6188c33..8942a6a4 100644 --- a/legacy/packages/core/src/task.ts +++ b/legacy/packages/core/src/task.ts @@ -1,3 +1,4 @@ +import EventEmitter from 'eventemitter3' import pRetry, { FailedAttemptError } from 'p-retry' import QuickLRU from 'quick-lru' import { ZodType } from 'zod' @@ -6,7 +7,7 @@ import * as errors from './errors' import * as types from './types' import type { Agentic } from './agentic' import { SKIP_HOOKS } from './constants' -import { TaskEventEmitter, TaskStatus } from './events' +import { TaskEvent, TaskStatus } from './events' import { HumanFeedbackMechanismCLI, HumanFeedbackOptions, @@ -30,14 +31,13 @@ import { defaultIDGeneratorFn, isValidTaskIdentifier } from './utils' export abstract class BaseTask< TInput extends types.TaskInput = void, TOutput extends types.TaskOutput = string -> { +> extends EventEmitter { protected _agentic: Agentic protected _id: string protected _timeoutMs?: number protected _retryConfig: types.RetryConfig protected _cacheConfig: types.CacheConfig - protected _eventEmitter: TaskEventEmitter protected _preHooks: Array<{ hook: types.TaskBeforeCallHook @@ -50,6 +50,8 @@ export abstract class BaseTask< }> = [] constructor(options: types.BaseTaskOptions = {}) { + super() + this._agentic = options.agentic ?? globalThis.__agentic?.deref() this._timeoutMs = options.timeoutMs @@ -73,11 +75,6 @@ export abstract class BaseTask< this._id = options.id ?? this._agentic?.idGeneratorFn() ?? defaultIDGeneratorFn() - - this._eventEmitter = new TaskEventEmitter( - this, - this._agentic - ) } public get agentic(): Agentic { @@ -96,10 +93,6 @@ export abstract class BaseTask< return this._agentic.logger } - public get eventEmitter(): TaskEventEmitter { - return this._eventEmitter - } - public abstract get inputSchema(): ZodType public abstract get outputSchema(): ZodType @@ -285,7 +278,7 @@ export abstract class BaseTask< } } - this._eventEmitter.emit(TaskStatus.RUNNING, { + this.emit(TaskStatus.RUNNING, { taskInputs: input, ...ctx.metadata }) @@ -360,7 +353,7 @@ export abstract class BaseTask< ctx.attemptNumber = err.attemptNumber + 1 ctx.metadata.error = err - this._eventEmitter.emit(TaskStatus.RETRYING, { + this.emit(TaskStatus.RETRYING, { taskInputs: input, taskOutput: err, ...ctx.metadata @@ -389,7 +382,7 @@ export abstract class BaseTask< // task for now. return } else { - this._eventEmitter.emit(TaskStatus.FAILED, { + this.emit(TaskStatus.FAILED, { taskInputs: input, taskOutput: err, ...ctx.metadata @@ -411,7 +404,7 @@ export abstract class BaseTask< // ctx.tracker.setOutput(stringifyForDebugging(result, { maxLength: 100 })) - this._eventEmitter.emit(TaskStatus.COMPLETED, { + this.emit(TaskStatus.COMPLETED, { taskInputs: input, taskOutput: result, ...ctx.metadata @@ -433,4 +426,33 @@ export abstract class BaseTask< // input: TInput, // onProgress: types.ProgressFunction // }): Promise + + on( + takStatus: T, + fn: (event: TaskEvent) => void, + context?: any + ): this { + return super.on(takStatus, fn, context) + } + + emit(taskStatus: string | symbol, payload: object = {}): boolean { + if (!Object.values(TaskStatus).includes(taskStatus as TaskStatus)) { + throw new Error(`Invalid task status: ${String(taskStatus)}`) + } + + const { id, nameForModel } = this + const event = new TaskEvent({ + payload: { + taskStatus: taskStatus as TaskStatus, + taskId: id, + taskName: nameForModel, + ...payload + } + }) + this._agentic.taskTracker.addEvent(event) + + this._agentic.emit(taskStatus, event) + + return super.emit(taskStatus, event) + } } From 419ba7347cc4d2132706d39b0e29ae13870bf405 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 5 Jul 2023 13:59:07 -0400 Subject: [PATCH 19/20] refactor: add isClosed property to tracker --- legacy/packages/core/src/events/tracker.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/legacy/packages/core/src/events/tracker.ts b/legacy/packages/core/src/events/tracker.ts index fe7a136f..1414acfc 100644 --- a/legacy/packages/core/src/events/tracker.ts +++ b/legacy/packages/core/src/events/tracker.ts @@ -34,6 +34,7 @@ export class TerminalTaskTracker { protected _viewMode = 'tasks' protected _outputs: Array = [] protected _renderingPaused = false + protected _isClosed = false protected _spinnerInterval: number protected _inactivityInterval: number @@ -120,9 +121,15 @@ export class TerminalTaskTracker { process.stdin.on('keypress', this.handleKeyPress) this.startInactivityTimeout() + + this._isClosed = false } close() { + if (this._isClosed) { + return + } + if (this._interval) { clearInterval(this._interval) } @@ -160,6 +167,8 @@ export class TerminalTaskTracker { // Pause the reading of stdin so that the Node.js process will exit once done: process.stdin.pause() + + this._isClosed = true } pause() { From 42c3c9309d33e2a7738e8a905e4eaa7b16f9d04e Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 5 Jul 2023 14:02:36 -0400 Subject: [PATCH 20/20] chore: add eventemitter3 dep --- legacy/packages/core/package.json | 1 + legacy/pnpm-lock.yaml | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/legacy/packages/core/package.json b/legacy/packages/core/package.json index 08ad14d5..4392ec74 100644 --- a/legacy/packages/core/package.json +++ b/legacy/packages/core/package.json @@ -45,6 +45,7 @@ "@types/json-schema": "^7.0.12", "colorette": "^2.0.20", "debug": "^4.3.4", + "eventemitter3": "^5.0.1", "expr-eval": "^2.0.2", "handlebars": "^4.7.7", "is-relative-url": "^4.0.0", diff --git a/legacy/pnpm-lock.yaml b/legacy/pnpm-lock.yaml index fb587442..9949707c 100644 --- a/legacy/pnpm-lock.yaml +++ b/legacy/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -125,6 +125,9 @@ importers: debug: specifier: ^4.3.4 version: 4.3.4 + eventemitter3: + specifier: ^5.0.1 + version: 5.0.1 expr-eval: specifier: ^2.0.2 version: 2.0.2 @@ -3225,6 +3228,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + /execa@0.8.0: resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==} engines: {node: '>=4'}