Skip to content

Commit

Permalink
Improve console output for CLI (#448)
Browse files Browse the repository at this point in the history
- Reduce concept and removed requestIO and using clientIO and the
command context directly.
- Forcing adjustment of the cursor on every input key to the cursor
problem when certain agent's emoji is displayed in the console prompt,
and node's r`eadline` didn't account for fullwidth of certain emoji
character.
- externalize tracking of history so that `askYesNo` input not be part
of the "history" by not sharing `readline`
  - Reduce lifetime of `readline` to just one question
instance.
- Allow Ctrl-C to work to kill the process during action by set `stdin`
to raw mode and resume the stream after `readline.question` so that we
can drain and continue to listen to input.
- Refactor and move console related code to `helpers/console` in for the
dispatcher.

Also:
- Allow boolean value "true" and "false" to be specified as value for
boolean flag (previously, having the flag means true, so no way to
specify false to turn the flag off)
  • Loading branch information
curtisman authored Dec 3, 2024
1 parent 3e230f2 commit 4755f1c
Show file tree
Hide file tree
Showing 30 changed files with 510 additions and 719 deletions.
1 change: 0 additions & 1 deletion ts/packages/agentSdk/src/display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export type ClientAction =
| "automate-phone-ui";

export interface ActionIO {
readonly type: DisplayType;
setDisplay(content: DisplayContent): void;

// Append content to the display, default mode is "inline"
Expand Down
8 changes: 0 additions & 8 deletions ts/packages/agents/browser/src/agent/actionHandler.mts
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,6 @@ async function updateBrowserContext(
break;
}
case "browserActionResponse": {
/*
const requestIO = context.requestIO;
const requestId = context.requestId;
if (requestIO && requestId && data.id === requestId) {
requestIO.success(data.body.message);
}
*/
break;
}
case "debugBrowserAction": {
Expand Down
24 changes: 0 additions & 24 deletions ts/packages/api/src/webClientIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,30 +147,6 @@ export class WebAPIClientIO implements ClientIO {
});
}

private maxQuestionId = 0;
question(message: string, requestId: RequestId): Promise<string> {
const currentQuestionId = this.maxQuestionId++;
return new Promise<string>((resolve) => {
this.questionCallbacks.set(
currentQuestionId,
(response: string) => {
resolve(response);
},
);

this.currentws?.send(
JSON.stringify({
message: "question",
data: {
currentQuestionId,
message,
requestId,
},
}),
);
});
}

private maxProposedActionId = 0;
proposeAction(
actionTemplates: TemplateEditConfig,
Expand Down
17 changes: 9 additions & 8 deletions ts/packages/cli/demo/demo.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# ====== Init =======
@session new --memory
@config action -*
@config schema -* player calendar
@session new --persist false
@config action -x *
@config schema -x *
@config schema player calendar
@config serviceHost off

# =============================================
Expand All @@ -16,23 +17,23 @@ Please play Nocturne No. 2 by Chopin.
@const list -a

# Cache hit
@config bot off
@config translation off
Could you play Anti-Hero by Taylor Swift?
Kindly play Rhapsody on a Theme of Paganini composed by Rachmaninoff?

# -------------------------------------
# Deep dive into explanation
# -------------------------------------
# Request with explanation
@config bot on
@config translation on
I would like to listen to 1812 Overture please.
@const list -a

# Cache hit
@config bot off
@config translation off
I would like to stream Figure Humaine by Poulenc
Could you play Liebesleid please?
@config bot on
@config translation on

# -------------------------------------
# Calendar - cross domain generalization
Expand All @@ -44,7 +45,7 @@ Can you schedule design meeting starting at 2 pm with Piali?
@const list -a

# Cache hit
@config bot off
@config translation off
kindly plan meeting to discuss office assignment from 4 pm with John?

# ==========================
Expand Down
14 changes: 5 additions & 9 deletions ts/packages/cli/src/commands/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
// Licensed under the MIT License.

import { Args, Command, Flags } from "@oclif/core";
import readline from "readline/promises";
import {
getBuiltinSchemaNames,
getCacheFactory,
processCommand,
processRequests,
getPrompt,
initializeCommandHandlerContext,
CommandHandlerContext,
closeCommandHandlerContext,
} from "agent-dispatcher/internal";
import inspector from "node:inspector";
import { getChatModelNames } from "aiclient";
import {
processRequests,
createConsoleClientIO,
} from "agent-dispatcher/helpers/console";

const modelNames = await getChatModelNames();

Expand Down Expand Up @@ -62,10 +64,6 @@ export default class Interactive extends Command {
if (flags.debug) {
inspector.open(undefined, undefined, true);
}
const stdio = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

let context: CommandHandlerContext | undefined;

Expand All @@ -77,9 +75,9 @@ export default class Interactive extends Command {
schemas,
translation: { model: flags.model },
explainer: { name: flags.explainer },
stdio,
persistSession: !flags.memory,
enableServiceHost: true,
clientIO: createConsoleClientIO(),
});

if (args.input) {
Expand All @@ -91,15 +89,13 @@ export default class Interactive extends Command {

await processRequests<CommandHandlerContext>(
getPrompt,
stdio,
processCommand,
context,
);
} finally {
if (context) {
await closeCommandHandlerContext(context);
}
stdio?.close();
}

// Some background network (like monogo) might keep the process live, exit explicitly.
Expand Down
4 changes: 3 additions & 1 deletion ts/packages/cli/src/commands/run/explain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
initializeCommandHandlerContext,
closeCommandHandlerContext,
} from "agent-dispatcher/internal";
import { createConsoleClientIO } from "agent-dispatcher/helpers/console";

// Default test case, that include multiple phrase action name (out of order) and implicit parameters (context)
const testRequest = new RequestAction(
Expand Down Expand Up @@ -81,7 +82,8 @@ export default class ExplainCommand extends Command {
name: flags.explainer,
},
cache: { enabled: false },
clientIO: flags.repeat > 1 ? null : undefined,
clientIO:
flags.repeat > 1 ? undefined : createConsoleClientIO(),
},
);

Expand Down
1 change: 1 addition & 0 deletions ts/packages/dispatcher/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"type": "module",
"exports": {
".": "./dist/index.js",
"./helpers/console": "./dist/helpers/console.js",
"./internal": "./dist/internal.js",
"./explorer": "./dist/explorer.js"
},
Expand Down
32 changes: 21 additions & 11 deletions ts/packages/dispatcher/src/action/actionHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { getUserProfileDir } from "../utils/userData.js";
import { IncrementalJsonValueCallBack } from "common-utils";
import { ProfileNames } from "../utils/profileNames.js";
import { conversation } from "knowledge-processor";
import { makeClientIOMessage } from "../handlers/common/interactiveIO.js";

const debugActions = registerDebug("typeagent:dispatcher:actions");

Expand All @@ -51,25 +52,34 @@ function getActionContext(
let context = systemContext;
const sessionContext = context.agents.getSessionContext(appAgentName);
const actionIO: ActionIO = {
get type() {
return context.requestIO.type;
},
setDisplay(content: DisplayContent): void {
context.requestIO.setDisplay(content, actionIndex, appAgentName);
context.clientIO.setDisplay(
makeClientIOMessage(
context,
content,
requestId,
appAgentName,
actionIndex,
),
);
},
appendDisplay(
content: DisplayContent,
mode: DisplayAppendMode = "inline",
): void {
context.requestIO.appendDisplay(
content,
actionIndex,
appAgentName,
context.clientIO.appendDisplay(
makeClientIOMessage(
context,
content,
requestId,
appAgentName,
actionIndex,
),
mode,
);
},
takeAction(action: string, data: unknown): void {
context.requestIO.takeAction(action, data);
context.clientIO.takeAction(action, data);
},
};
const actionContext: ActionContext<unknown> = {
Expand Down Expand Up @@ -126,7 +136,7 @@ export function createSessionContext<T = unknown>(
return profileStorage;
},
notify(event: AppAgentEvent, message: string) {
context.requestIO.notify(event, undefined, message, name);
context.clientIO.notify(event, undefined, message, name);
},
async toggleTransientAgent(subAgentName: string, enable: boolean) {
if (!subAgentName.startsWith(`${name}.`)) {
Expand Down Expand Up @@ -255,7 +265,7 @@ async function executeAction(
actionContext.actionIO.setDisplay(result.displayContent);
}
if (result.dynamicDisplayId !== undefined) {
systemContext.clientIO?.setDynamicDisplay(
systemContext.clientIO.setDynamicDisplay(
translatorName,
systemContext.requestId,
actionIndex,
Expand Down
3 changes: 0 additions & 3 deletions ts/packages/dispatcher/src/agent/agentProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@ function getActionContextShim(
}
const sessionContext = getSessionContextShim(param);
const actionIO: ActionIO = {
get type(): DisplayType {
return "text";
},
setDisplay(content: DisplayContent): void {
rpc.send("setDisplay", {
actionContextId,
Expand Down
19 changes: 7 additions & 12 deletions ts/packages/dispatcher/src/agent/inlineAgentHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ import { getHistoryCommandHandlers } from "../handlers/historyCommandHandler.js"
import { TraceCommandHandler } from "../handlers/traceCommandHandler.js";
import { getRandomCommandHandlers } from "../handlers/randomCommandHandler.js";
import { getNotifyCommandHandlers } from "../handlers/notifyCommandHandler.js";
import { processRequests } from "../utils/interactive.js";
import { getConsoleRequestIO } from "../handlers/common/interactiveIO.js";
import { processRequests } from "agent-dispatcher/helpers/console";
import {
getDefaultSubCommandDescriptor,
getParsedCommand,
Expand Down Expand Up @@ -153,24 +152,22 @@ class RunCommandScriptHandler implements CommandHandler {
const inputFile = path.resolve(prevScriptDir, params.args.input);
const content = await fs.promises.readFile(inputFile, "utf8");
const inputs = content.split(/\r?\n/);
const prevRequestIO = systemContext.requestIO;
const prevBatchMode = systemContext.batchMode;
try {
// handle nested @run in files
systemContext.currentScriptDir = path.parse(inputFile).dir;

// Disable confirmation in file mode
systemContext.requestIO = getConsoleRequestIO(undefined);
systemContext.batchMode = true;

// Process the commands in the file.
await processRequests(
getPrompt,
inputs,
processCommandNoLock,
systemContext,
inputs,
);
} finally {
// Restore state
systemContext.requestIO = prevRequestIO;
systemContext.batchMode = prevBatchMode;
systemContext.currentScriptDir = prevScriptDir;
}
}
Expand Down Expand Up @@ -336,17 +333,15 @@ const systemHandlers: CommandHandlerTable = {
clear: {
description: "Clear the console",
async run(context: ActionContext<CommandHandlerContext>) {
context.sessionContext.agentContext.requestIO.clear();
context.sessionContext.agentContext.clientIO.clear();
},
},
run: new RunCommandScriptHandler(),
exit: {
description: "Exit the program",
async run(context: ActionContext<CommandHandlerContext>) {
const systemContext = context.sessionContext.agentContext;
systemContext.clientIO
? systemContext.clientIO.exit()
: process.exit(0);
systemContext.clientIO.exit();
},
},
random: getRandomCommandHandlers(),
Expand Down
Loading

0 comments on commit 4755f1c

Please sign in to comment.