Skip to content

Commit 4e592ab

Browse files
committed
hook main loop from mcp-client/cli.ts
1 parent bb2f36e commit 4e592ab

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

packages/tiny-agents/src/cli.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { PROVIDERS_OR_POLICIES } from "@huggingface/inference";
77
import { Agent } from "@huggingface/mcp-client";
88
import { version as packageVersion } from "../package.json";
99
import { ServerConfigSchema } from "./lib/types";
10-
import { ANSI, debug, error } from "./lib/utils";
10+
import { debug, error } from "./lib/utils";
11+
import { mainCliLoop } from "./lib/mainCliLoop";
1112

1213
const USAGE_HELP = `
1314
Usage:
@@ -122,9 +123,9 @@ async function main() {
122123
error(`Serve is not implemented yet, coming soon!`);
123124
process.exit(1);
124125
} else {
125-
console.debug(agent);
126-
127-
// TODO: hook main loop from mcp-client/cli.ts
126+
debug(agent);
127+
// main loop from mcp-client/cli.ts
128+
await mainCliLoop(agent);
128129
}
129130
}
130131

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import * as readline from "node:readline/promises";
2+
import { stdin, stdout } from "node:process";
3+
import type { Agent } from "@huggingface/mcp-client";
4+
import { ANSI } from "./utils";
5+
6+
/**
7+
* From mcp-client/cli.ts
8+
*/
9+
export async function mainCliLoop(agent: Agent): Promise<void> {
10+
const rl = readline.createInterface({ input: stdin, output: stdout });
11+
let abortController = new AbortController();
12+
let waitingForInput = false;
13+
async function waitForInput() {
14+
waitingForInput = true;
15+
const input = await rl.question("> ");
16+
waitingForInput = false;
17+
return input;
18+
}
19+
rl.on("SIGINT", async () => {
20+
if (waitingForInput) {
21+
// close the whole process
22+
await agent.cleanup();
23+
stdout.write("\n");
24+
rl.close();
25+
} else {
26+
// otherwise, it means a request is underway
27+
abortController.abort();
28+
abortController = new AbortController();
29+
stdout.write("\n");
30+
stdout.write(ANSI.GRAY);
31+
stdout.write("Ctrl+C a second time to exit");
32+
stdout.write(ANSI.RESET);
33+
stdout.write("\n");
34+
}
35+
});
36+
process.on("uncaughtException", (err) => {
37+
stdout.write("\n");
38+
rl.close();
39+
throw err;
40+
});
41+
42+
await agent.loadTools();
43+
44+
stdout.write(ANSI.BLUE);
45+
stdout.write(`Agent loaded with ${agent.availableTools.length} tools:\n`);
46+
stdout.write(agent.availableTools.map((t) => `- ${t.function.name}`).join("\n"));
47+
stdout.write(ANSI.RESET);
48+
stdout.write("\n");
49+
50+
while (true) {
51+
const input = await waitForInput();
52+
for await (const chunk of agent.run(input, { abortSignal: abortController.signal })) {
53+
if ("choices" in chunk) {
54+
const delta = chunk.choices[0]?.delta;
55+
if (delta.content) {
56+
stdout.write(delta.content);
57+
}
58+
if (delta.tool_calls) {
59+
stdout.write(ANSI.GRAY);
60+
for (const deltaToolCall of delta.tool_calls) {
61+
if (deltaToolCall.id) {
62+
stdout.write(`<Tool ${deltaToolCall.id}>\n`);
63+
}
64+
if (deltaToolCall.function.name) {
65+
stdout.write(deltaToolCall.function.name + " ");
66+
}
67+
if (deltaToolCall.function.arguments) {
68+
stdout.write(deltaToolCall.function.arguments);
69+
}
70+
}
71+
stdout.write(ANSI.RESET);
72+
}
73+
} else {
74+
/// Tool call info
75+
stdout.write("\n\n");
76+
stdout.write(ANSI.GREEN);
77+
stdout.write(`Tool[${chunk.name}] ${chunk.tool_call_id}\n`);
78+
stdout.write(chunk.content);
79+
stdout.write(ANSI.RESET);
80+
stdout.write("\n\n");
81+
}
82+
}
83+
stdout.write("\n");
84+
}
85+
}

0 commit comments

Comments
 (0)