Repository navigation
Expand file tree
/
Copy pathchat.js
More file actions
128 lines (105 loc) · 4.56 KB
/
Copy pathchat.js
File metadata and controls
128 lines (105 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env node
/**
* Interactive console chat using pilotswarm.
*
* Usage: node --env-file=.env examples/chat.js
*
* Features:
* - Full architecture (orchestration → SessionProxy → ManagedSession)
* - CMS session persistence (Postgres)
* - Live event streaming via session.on()
* - Durable wait tool + ask_user tool
*/
import { PilotSwarmClient, PilotSwarmWorker } from "pilotswarm-sdk";
import { createInterface } from "node:readline";
const STORE = process.env.DATABASE_URL;
if (!STORE) {
throw new Error("Missing DATABASE_URL. PilotSwarm requires PostgreSQL for CMS and facts.");
}
// ─── Model selection (--model=provider:model or bare model name) ─
const MODEL = process.argv.find(a => a.startsWith('--model='))?.split('=')[1] || undefined;
if (MODEL) console.log(` Model: ${MODEL}`);
// ─── Readline setup ──────────────────────────────────────────────
const rl = createInterface({ input: process.stdin, output: process.stdout });
const prompt = (q) => new Promise((resolve) => rl.question(q, resolve));
function stripMarkdown(text) {
return text
.replace(/\*\*(.+?)\*\*/g, "$1")
.replace(/\*(.+?)\*/g, "$1")
.replace(/`(.+?)`/g, "$1")
.replace(/^#{1,6}\s+/gm, "")
.replace(/^\s*[-*+]\s+/gm, " • ")
.replace(/\[(.+?)\]\(.+?\)/g, "$1")
.trim();
}
// ─── Start worker + client ───────────────────────────────────────
console.log("🤖 PilotSwarm");
console.log(` Store: ${STORE.startsWith("postgres") ? "PostgreSQL" : STORE}\n`);
const worker = new PilotSwarmWorker({
store: STORE,
githubToken: process.env.GITHUB_TOKEN,
blobConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
blobContainer: process.env.AZURE_STORAGE_CONTAINER || "copilot-sessions",
});
await worker.start();
const client = new PilotSwarmClient({
store: STORE,
});
await client.start();
// Create session with user input handler for ask_user tool
const session = await client.createSession({
...(MODEL ? { model: MODEL } : {}),
onUserInputRequest: async (request) => {
let q = `\n❓ ${request.question}`;
if (request.choices?.length) {
q += "\n" + request.choices.map((c, i) => ` ${i + 1}. ${c}`).join("\n");
}
q += "\n> ";
const answer = await prompt(q);
return { answer };
},
});
// Forward config to co-located worker
worker.setSessionConfig(session.sessionId, {});
// ─── Subscribe to live events ────────────────────────────────────
session.on((event) => {
const dim = "\x1b[2m";
const reset = "\x1b[0m";
const type = event.eventType;
const seq = event.seq;
// Compact one-line display per event
let detail = "";
if (type === "assistant.message") {
const content = event.data?.content ?? "";
detail = content.length > 80 ? content.slice(0, 80) + "…" : content;
} else if (type === "tool.execution_start") {
detail = event.data?.toolName ?? event.data?.name ?? "";
} else if (type === "tool.execution_end") {
detail = event.data?.toolName ?? event.data?.name ?? "";
} else if (type === "assistant.usage" || type === "session.usage_info") {
const u = event.data;
detail = u ? `in=${u.inputTokens ?? u.input_tokens ?? "?"} out=${u.outputTokens ?? u.output_tokens ?? "?"}` : "";
}
console.log(`${dim} [${seq}] ${type}${detail ? " — " + detail : ""}${reset}`);
});
// ─── Chat loop ───────────────────────────────────────────────────
console.log(" Type a message, press Enter. Type 'exit' to quit.\n");
while (true) {
const input = await prompt("you> ");
if (!input || input.trim().toLowerCase() === "exit") break;
try {
const response = await session.sendAndWait(input.trim(), 300_000);
if (response) {
console.log(`\n${stripMarkdown(response)}\n`);
}
} catch (err) {
console.error(`\n⚠️ Error: ${err.message}\n`);
}
}
// ─── Cleanup ─────────────────────────────────────────────────────
console.log("\n👋 Bye!");
await session.destroy();
await client.stop();
await worker.stop();
rl.close();
process.exit(0);