llmist 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ import {
9
9
  init_constants,
10
10
  init_gadget,
11
11
  init_logger
12
- } from "./chunk-KORMY3CD.js";
12
+ } from "./chunk-RZTAKIDE.js";
13
13
 
14
14
  // src/gadgets/validation.ts
15
15
  function validateAndApplyDefaults(schema, params) {
@@ -917,6 +917,437 @@ function mockGadget() {
917
917
  return new MockGadgetBuilder();
918
918
  }
919
919
 
920
+ // src/testing/stream-helpers.ts
921
+ function createTestStream(chunks) {
922
+ return async function* () {
923
+ for (const chunk of chunks) {
924
+ yield chunk;
925
+ }
926
+ }();
927
+ }
928
+ function createTextStream(text, options) {
929
+ return async function* () {
930
+ if (options?.delayMs) {
931
+ await sleep2(options.delayMs);
932
+ }
933
+ const chunkSize = options?.chunkSize ?? text.length;
934
+ const chunks = [];
935
+ for (let i = 0; i < text.length; i += chunkSize) {
936
+ chunks.push(text.slice(i, i + chunkSize));
937
+ }
938
+ for (let i = 0; i < chunks.length; i++) {
939
+ const isLast = i === chunks.length - 1;
940
+ const chunk = { text: chunks[i] };
941
+ if (isLast) {
942
+ chunk.finishReason = options?.finishReason ?? "stop";
943
+ const inputTokens = Math.ceil(text.length / 4);
944
+ const outputTokens = Math.ceil(text.length / 4);
945
+ chunk.usage = options?.usage ?? {
946
+ inputTokens,
947
+ outputTokens,
948
+ totalTokens: inputTokens + outputTokens
949
+ };
950
+ }
951
+ yield chunk;
952
+ if (options?.chunkDelayMs && !isLast) {
953
+ await sleep2(options.chunkDelayMs);
954
+ }
955
+ }
956
+ }();
957
+ }
958
+ async function collectStream(stream) {
959
+ const chunks = [];
960
+ for await (const chunk of stream) {
961
+ chunks.push(chunk);
962
+ }
963
+ return chunks;
964
+ }
965
+ async function collectStreamText(stream) {
966
+ let text = "";
967
+ for await (const chunk of stream) {
968
+ text += chunk.text ?? "";
969
+ }
970
+ return text;
971
+ }
972
+ async function getStreamFinalChunk(stream) {
973
+ let lastChunk;
974
+ for await (const chunk of stream) {
975
+ lastChunk = chunk;
976
+ }
977
+ return lastChunk;
978
+ }
979
+ function createEmptyStream() {
980
+ return async function* () {
981
+ }();
982
+ }
983
+ function createErrorStream(chunksBeforeError, error) {
984
+ return async function* () {
985
+ for (const chunk of chunksBeforeError) {
986
+ yield chunk;
987
+ }
988
+ throw error;
989
+ }();
990
+ }
991
+ function sleep2(ms) {
992
+ return new Promise((resolve) => setTimeout(resolve, ms));
993
+ }
994
+
995
+ // src/testing/conversation-fixtures.ts
996
+ function createConversation(turnCount, options) {
997
+ const messages = [];
998
+ const userPrefix = options?.userPrefix ?? "User message";
999
+ const assistantPrefix = options?.assistantPrefix ?? "Assistant response";
1000
+ const contentLength = options?.contentLength ?? 100;
1001
+ for (let i = 0; i < turnCount; i++) {
1002
+ const padding = " ".repeat(Math.max(0, contentLength - 30));
1003
+ messages.push({
1004
+ role: "user",
1005
+ content: `${userPrefix} ${i + 1}: This is turn ${i + 1} of the conversation.${padding}`
1006
+ });
1007
+ messages.push({
1008
+ role: "assistant",
1009
+ content: `${assistantPrefix} ${i + 1}: I acknowledge turn ${i + 1}.${padding}`
1010
+ });
1011
+ }
1012
+ return messages;
1013
+ }
1014
+ function createConversationWithGadgets(turnCount, gadgetCallsPerTurn = 1, options) {
1015
+ const messages = [];
1016
+ const gadgetNames = options?.gadgetNames ?? ["search", "calculate", "read"];
1017
+ const contentLength = options?.contentLength ?? 50;
1018
+ let gadgetIndex = 0;
1019
+ for (let turn = 0; turn < turnCount; turn++) {
1020
+ messages.push({
1021
+ role: "user",
1022
+ content: `User request ${turn + 1}${"x".repeat(contentLength)}`
1023
+ });
1024
+ for (let g = 0; g < gadgetCallsPerTurn; g++) {
1025
+ const gadgetName = gadgetNames[gadgetIndex % gadgetNames.length];
1026
+ gadgetIndex++;
1027
+ messages.push({
1028
+ role: "assistant",
1029
+ content: `!!!GADGET_START:${gadgetName}
1030
+ !!!ARG:query
1031
+ test query ${turn}-${g}
1032
+ !!!GADGET_END`
1033
+ });
1034
+ messages.push({
1035
+ role: "user",
1036
+ content: `Result: Gadget ${gadgetName} returned result for query ${turn}-${g}`
1037
+ });
1038
+ }
1039
+ messages.push({
1040
+ role: "assistant",
1041
+ content: `Final response for turn ${turn + 1}${"y".repeat(contentLength)}`
1042
+ });
1043
+ }
1044
+ return messages;
1045
+ }
1046
+ function estimateTokens(messages) {
1047
+ return Math.ceil(
1048
+ messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
1049
+ );
1050
+ }
1051
+ function createUserMessage(content) {
1052
+ return { role: "user", content };
1053
+ }
1054
+ function createAssistantMessage(content) {
1055
+ return { role: "assistant", content };
1056
+ }
1057
+ function createSystemMessage(content) {
1058
+ return { role: "system", content };
1059
+ }
1060
+ function createMinimalConversation() {
1061
+ return [
1062
+ { role: "user", content: "Hello" },
1063
+ { role: "assistant", content: "Hi there!" }
1064
+ ];
1065
+ }
1066
+ function createLargeConversation(targetTokens, options) {
1067
+ const tokensPerTurn = options?.tokensPerTurn ?? 200;
1068
+ const turnsNeeded = Math.ceil(targetTokens / tokensPerTurn);
1069
+ const charsPerMessage = Math.floor(tokensPerTurn * 4 / 2);
1070
+ return createConversation(turnsNeeded, {
1071
+ contentLength: charsPerMessage
1072
+ });
1073
+ }
1074
+
1075
+ // src/testing/mock-conversation.ts
1076
+ var MockConversationManager = class {
1077
+ history;
1078
+ baseMessages;
1079
+ replacementHistory;
1080
+ replaceHistoryCallCount = 0;
1081
+ addedMessages = [];
1082
+ constructor(history = [], baseMessages = []) {
1083
+ this.history = [...history];
1084
+ this.baseMessages = [...baseMessages];
1085
+ }
1086
+ addUserMessage(content) {
1087
+ const msg = { role: "user", content };
1088
+ this.history.push(msg);
1089
+ this.addedMessages.push(msg);
1090
+ }
1091
+ addAssistantMessage(content) {
1092
+ const msg = { role: "assistant", content };
1093
+ this.history.push(msg);
1094
+ this.addedMessages.push(msg);
1095
+ }
1096
+ addGadgetCall(gadgetName, parameters, result) {
1097
+ const assistantMsg = {
1098
+ role: "assistant",
1099
+ content: `!!!GADGET_START:${gadgetName}
1100
+ ${JSON.stringify(parameters)}
1101
+ !!!GADGET_END`
1102
+ };
1103
+ const resultMsg = {
1104
+ role: "user",
1105
+ content: `Result: ${result}`
1106
+ };
1107
+ this.history.push(assistantMsg);
1108
+ this.history.push(resultMsg);
1109
+ this.addedMessages.push(assistantMsg);
1110
+ this.addedMessages.push(resultMsg);
1111
+ }
1112
+ getMessages() {
1113
+ return [...this.baseMessages, ...this.history];
1114
+ }
1115
+ getHistoryMessages() {
1116
+ return [...this.history];
1117
+ }
1118
+ getBaseMessages() {
1119
+ return [...this.baseMessages];
1120
+ }
1121
+ replaceHistory(newHistory) {
1122
+ this.replacementHistory = [...newHistory];
1123
+ this.history = [...newHistory];
1124
+ this.replaceHistoryCallCount++;
1125
+ }
1126
+ // ============================================
1127
+ // Test Helper Methods
1128
+ // ============================================
1129
+ /**
1130
+ * Check if replaceHistory was called.
1131
+ */
1132
+ wasReplaceHistoryCalled() {
1133
+ return this.replaceHistoryCallCount > 0;
1134
+ }
1135
+ /**
1136
+ * Get the number of times replaceHistory was called.
1137
+ */
1138
+ getReplaceHistoryCallCount() {
1139
+ return this.replaceHistoryCallCount;
1140
+ }
1141
+ /**
1142
+ * Get the most recent history passed to replaceHistory.
1143
+ * Returns undefined if replaceHistory was never called.
1144
+ */
1145
+ getReplacementHistory() {
1146
+ return this.replacementHistory;
1147
+ }
1148
+ /**
1149
+ * Get all messages that were added via add* methods.
1150
+ */
1151
+ getAddedMessages() {
1152
+ return [...this.addedMessages];
1153
+ }
1154
+ /**
1155
+ * Reset all tracking state while preserving the conversation.
1156
+ */
1157
+ resetTracking() {
1158
+ this.replacementHistory = void 0;
1159
+ this.replaceHistoryCallCount = 0;
1160
+ this.addedMessages = [];
1161
+ }
1162
+ /**
1163
+ * Completely reset the mock to initial state.
1164
+ * Note: baseMessages cannot be changed after construction.
1165
+ */
1166
+ reset(history = []) {
1167
+ this.history = [...history];
1168
+ this.resetTracking();
1169
+ }
1170
+ /**
1171
+ * Set the history directly (for test setup).
1172
+ */
1173
+ setHistory(messages) {
1174
+ this.history = [...messages];
1175
+ }
1176
+ /**
1177
+ * Get the current history length.
1178
+ */
1179
+ getHistoryLength() {
1180
+ return this.history.length;
1181
+ }
1182
+ /**
1183
+ * Get total message count (base + history).
1184
+ */
1185
+ getTotalMessageCount() {
1186
+ return this.baseMessages.length + this.history.length;
1187
+ }
1188
+ };
1189
+ function createMockConversationManager(turnCount, baseMessages = []) {
1190
+ const history = [];
1191
+ for (let i = 0; i < turnCount; i++) {
1192
+ history.push({
1193
+ role: "user",
1194
+ content: `User message ${i + 1}: This is turn ${i + 1} of the conversation.`
1195
+ });
1196
+ history.push({
1197
+ role: "assistant",
1198
+ content: `Assistant response ${i + 1}: I acknowledge turn ${i + 1}.`
1199
+ });
1200
+ }
1201
+ return new MockConversationManager(history, baseMessages);
1202
+ }
1203
+
1204
+ // src/testing/cli-helpers.ts
1205
+ import { PassThrough, Readable, Writable } from "node:stream";
1206
+ function createTestEnvironment(options = {}) {
1207
+ const stdin = createMockReadable(options.stdin);
1208
+ const stdout = new PassThrough();
1209
+ const stderr = new PassThrough();
1210
+ let exitCode;
1211
+ return {
1212
+ stdin,
1213
+ stdout,
1214
+ stderr,
1215
+ isTTY: options.isTTY ?? false,
1216
+ argv: options.argv ?? ["node", "llmist"],
1217
+ env: { ...filterDefinedEnv(process.env), ...options.env },
1218
+ get exitCode() {
1219
+ return exitCode;
1220
+ },
1221
+ setExitCode: (code) => {
1222
+ exitCode = code;
1223
+ }
1224
+ };
1225
+ }
1226
+ function createMockReadable(input) {
1227
+ if (!input) {
1228
+ const stream2 = new Readable({ read() {
1229
+ } });
1230
+ stream2.push(null);
1231
+ return stream2;
1232
+ }
1233
+ const content = Array.isArray(input) ? `${input.join("\n")}
1234
+ ` : input;
1235
+ const stream = new Readable({ read() {
1236
+ } });
1237
+ stream.push(content);
1238
+ stream.push(null);
1239
+ return stream;
1240
+ }
1241
+ function createMockWritable() {
1242
+ const chunks = [];
1243
+ const stream = new Writable({
1244
+ write(chunk, _encoding, callback) {
1245
+ chunks.push(Buffer.from(chunk));
1246
+ callback();
1247
+ }
1248
+ });
1249
+ stream.getData = () => Buffer.concat(chunks).toString("utf8");
1250
+ return stream;
1251
+ }
1252
+ async function collectOutput(stream, timeout = 5e3) {
1253
+ return new Promise((resolve, reject) => {
1254
+ const chunks = [];
1255
+ const timeoutId = setTimeout(() => {
1256
+ resolve(Buffer.concat(chunks).toString("utf8"));
1257
+ }, timeout);
1258
+ stream.on("data", (chunk) => {
1259
+ chunks.push(Buffer.from(chunk));
1260
+ });
1261
+ stream.on("end", () => {
1262
+ clearTimeout(timeoutId);
1263
+ resolve(Buffer.concat(chunks).toString("utf8"));
1264
+ });
1265
+ stream.on("error", (err) => {
1266
+ clearTimeout(timeoutId);
1267
+ reject(err);
1268
+ });
1269
+ });
1270
+ }
1271
+ function getBufferedOutput(stream) {
1272
+ const chunks = [];
1273
+ for (; ; ) {
1274
+ const chunk = stream.read();
1275
+ if (chunk === null) break;
1276
+ chunks.push(chunk);
1277
+ }
1278
+ return Buffer.concat(chunks).toString("utf8");
1279
+ }
1280
+ function createMockPrompt(responses) {
1281
+ let index = 0;
1282
+ return async (_question) => {
1283
+ if (index >= responses.length) {
1284
+ throw new Error(`Mock prompt exhausted: no response for question ${index + 1}`);
1285
+ }
1286
+ return responses[index++];
1287
+ };
1288
+ }
1289
+ var MockPromptRecorder = class {
1290
+ responses;
1291
+ index = 0;
1292
+ questions = [];
1293
+ constructor(responses) {
1294
+ this.responses = responses;
1295
+ }
1296
+ /**
1297
+ * The prompt function to use in tests.
1298
+ */
1299
+ prompt = async (question) => {
1300
+ this.questions.push(question);
1301
+ if (this.index >= this.responses.length) {
1302
+ throw new Error(`Mock prompt exhausted after ${this.index} questions`);
1303
+ }
1304
+ return this.responses[this.index++];
1305
+ };
1306
+ /**
1307
+ * Get all questions that were asked.
1308
+ */
1309
+ getQuestions() {
1310
+ return [...this.questions];
1311
+ }
1312
+ /**
1313
+ * Get the number of questions asked.
1314
+ */
1315
+ getQuestionCount() {
1316
+ return this.questions.length;
1317
+ }
1318
+ /**
1319
+ * Reset the recorder state.
1320
+ */
1321
+ reset(newResponses) {
1322
+ this.index = 0;
1323
+ this.questions = [];
1324
+ if (newResponses) {
1325
+ this.responses = newResponses;
1326
+ }
1327
+ }
1328
+ };
1329
+ async function waitFor(condition, timeout = 5e3, interval = 50) {
1330
+ const startTime = Date.now();
1331
+ while (!condition()) {
1332
+ if (Date.now() - startTime > timeout) {
1333
+ throw new Error(`waitFor timed out after ${timeout}ms`);
1334
+ }
1335
+ await sleep3(interval);
1336
+ }
1337
+ }
1338
+ function sleep3(ms) {
1339
+ return new Promise((resolve) => setTimeout(resolve, ms));
1340
+ }
1341
+ function filterDefinedEnv(env) {
1342
+ const result = {};
1343
+ for (const [key, value] of Object.entries(env)) {
1344
+ if (value !== void 0) {
1345
+ result[key] = value;
1346
+ }
1347
+ }
1348
+ return result;
1349
+ }
1350
+
920
1351
  export {
921
1352
  validateAndApplyDefaults,
922
1353
  validateGadgetParams,
@@ -933,6 +1364,31 @@ export {
933
1364
  createMockClient,
934
1365
  createMockGadget,
935
1366
  MockGadgetBuilder,
936
- mockGadget
1367
+ mockGadget,
1368
+ createTestStream,
1369
+ createTextStream,
1370
+ collectStream,
1371
+ collectStreamText,
1372
+ getStreamFinalChunk,
1373
+ createEmptyStream,
1374
+ createErrorStream,
1375
+ createConversation,
1376
+ createConversationWithGadgets,
1377
+ estimateTokens,
1378
+ createUserMessage,
1379
+ createAssistantMessage,
1380
+ createSystemMessage,
1381
+ createMinimalConversation,
1382
+ createLargeConversation,
1383
+ MockConversationManager,
1384
+ createMockConversationManager,
1385
+ createTestEnvironment,
1386
+ createMockReadable,
1387
+ createMockWritable,
1388
+ collectOutput,
1389
+ getBufferedOutput,
1390
+ createMockPrompt,
1391
+ MockPromptRecorder,
1392
+ waitFor
937
1393
  };
938
- //# sourceMappingURL=chunk-LELPPETT.js.map
1394
+ //# sourceMappingURL=chunk-TFIKR2RK.js.map