@gmickel/gno 0.17.0 → 0.19.0

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.
@@ -66,6 +66,8 @@ export interface SearchRequestBody {
66
66
  limit?: number;
67
67
  minScore?: number;
68
68
  collection?: string;
69
+ intent?: string;
70
+ exclude?: string;
69
71
  since?: string;
70
72
  until?: string;
71
73
  /** Comma-separated category filters */
@@ -83,6 +85,9 @@ export interface QueryRequestBody {
83
85
  minScore?: number;
84
86
  collection?: string;
85
87
  lang?: string;
88
+ intent?: string;
89
+ candidateLimit?: number;
90
+ exclude?: string;
86
91
  since?: string;
87
92
  until?: string;
88
93
  /** Comma-separated category filters */
@@ -102,6 +107,10 @@ export interface AskRequestBody {
102
107
  limit?: number;
103
108
  collection?: string;
104
109
  lang?: string;
110
+ intent?: string;
111
+ candidateLimit?: number;
112
+ exclude?: string;
113
+ queryModes?: QueryModeInput[];
105
114
  since?: string;
106
115
  until?: string;
107
116
  /** Comma-separated category filters */
@@ -169,6 +178,73 @@ function parseCommaSeparatedValues(input: string): string[] {
169
178
  );
170
179
  }
171
180
 
181
+ function parseQueryModesInput(value: unknown): {
182
+ queryModes?: QueryModeInput[];
183
+ error?: Response;
184
+ } {
185
+ if (value === undefined) {
186
+ return {};
187
+ }
188
+
189
+ if (!Array.isArray(value)) {
190
+ return {
191
+ error: errorResponse(
192
+ "VALIDATION",
193
+ "queryModes must be an array of { mode, text } objects"
194
+ ),
195
+ };
196
+ }
197
+
198
+ const queryModes: QueryModeInput[] = [];
199
+ let hydeCount = 0;
200
+
201
+ for (const [index, entry] of value.entries()) {
202
+ if (!entry || typeof entry !== "object") {
203
+ return {
204
+ error: errorResponse(
205
+ "VALIDATION",
206
+ `queryModes[${index}] must be an object`
207
+ ),
208
+ };
209
+ }
210
+
211
+ const mode = (entry as { mode?: unknown }).mode;
212
+ const text = (entry as { text?: unknown }).text;
213
+ if (mode !== "term" && mode !== "intent" && mode !== "hyde") {
214
+ return {
215
+ error: errorResponse(
216
+ "VALIDATION",
217
+ `queryModes[${index}].mode must be one of: term, intent, hyde`
218
+ ),
219
+ };
220
+ }
221
+ if (typeof text !== "string" || !text.trim()) {
222
+ return {
223
+ error: errorResponse(
224
+ "VALIDATION",
225
+ `queryModes[${index}].text must be a non-empty string`
226
+ ),
227
+ };
228
+ }
229
+
230
+ if (mode === "hyde") {
231
+ hydeCount += 1;
232
+ if (hydeCount > 1) {
233
+ return {
234
+ error: errorResponse(
235
+ "VALIDATION",
236
+ "Only one hyde mode is allowed in queryModes"
237
+ ),
238
+ };
239
+ }
240
+ }
241
+
242
+ queryModes.push({ mode, text: text.trim() });
243
+ }
244
+
245
+ return { queryModes };
246
+ }
247
+
172
248
  // ─────────────────────────────────────────────────────────────────────────────
173
249
  // Route Handlers
174
250
  // ─────────────────────────────────────────────────────────────────────────────
@@ -1093,6 +1169,15 @@ export async function handleSearch(
1093
1169
  if (body.until !== undefined && typeof body.until !== "string") {
1094
1170
  return errorResponse("VALIDATION", "until must be a string");
1095
1171
  }
1172
+ if (body.intent !== undefined && typeof body.intent !== "string") {
1173
+ return errorResponse("VALIDATION", "intent must be a string");
1174
+ }
1175
+ if (body.exclude !== undefined && typeof body.exclude !== "string") {
1176
+ return errorResponse(
1177
+ "VALIDATION",
1178
+ "exclude must be a comma-separated string"
1179
+ );
1180
+ }
1096
1181
  if (body.category !== undefined && typeof body.category !== "string") {
1097
1182
  return errorResponse(
1098
1183
  "VALIDATION",
@@ -1132,6 +1217,9 @@ export async function handleSearch(
1132
1217
  const categories = body.category
1133
1218
  ? parseCommaSeparatedValues(body.category)
1134
1219
  : undefined;
1220
+ const exclude = body.exclude
1221
+ ? parseCommaSeparatedValues(body.exclude)
1222
+ : undefined;
1135
1223
  const author = body.author?.trim() || undefined;
1136
1224
 
1137
1225
  // Only BM25 supported in web UI (vector/hybrid require LLM ports)
@@ -1139,6 +1227,8 @@ export async function handleSearch(
1139
1227
  limit: Math.min(body.limit || 10, 50),
1140
1228
  minScore: body.minScore,
1141
1229
  collection: body.collection,
1230
+ intent: body.intent?.trim() || undefined,
1231
+ exclude,
1142
1232
  tagsAll,
1143
1233
  tagsAny,
1144
1234
  since: body.since,
@@ -1208,6 +1298,24 @@ export async function handleQuery(
1208
1298
  if (body.until !== undefined && typeof body.until !== "string") {
1209
1299
  return errorResponse("VALIDATION", "until must be a string");
1210
1300
  }
1301
+ if (body.intent !== undefined && typeof body.intent !== "string") {
1302
+ return errorResponse("VALIDATION", "intent must be a string");
1303
+ }
1304
+ if (body.exclude !== undefined && typeof body.exclude !== "string") {
1305
+ return errorResponse(
1306
+ "VALIDATION",
1307
+ "exclude must be a comma-separated string"
1308
+ );
1309
+ }
1310
+ if (
1311
+ body.candidateLimit !== undefined &&
1312
+ (typeof body.candidateLimit !== "number" || body.candidateLimit < 1)
1313
+ ) {
1314
+ return errorResponse(
1315
+ "VALIDATION",
1316
+ "candidateLimit must be a positive integer"
1317
+ );
1318
+ }
1211
1319
  if (body.category !== undefined && typeof body.category !== "string") {
1212
1320
  return errorResponse(
1213
1321
  "VALIDATION",
@@ -1218,54 +1326,11 @@ export async function handleQuery(
1218
1326
  return errorResponse("VALIDATION", "author must be a string");
1219
1327
  }
1220
1328
 
1221
- // Validate queryModes
1222
- let queryModes: QueryModeInput[] | undefined;
1223
- if (body.queryModes !== undefined) {
1224
- if (!Array.isArray(body.queryModes)) {
1225
- return errorResponse(
1226
- "VALIDATION",
1227
- "queryModes must be an array of { mode, text } objects"
1228
- );
1229
- }
1230
-
1231
- queryModes = [];
1232
- let hydeCount = 0;
1233
-
1234
- for (const [index, entry] of body.queryModes.entries()) {
1235
- if (!entry || typeof entry !== "object") {
1236
- return errorResponse(
1237
- "VALIDATION",
1238
- `queryModes[${index}] must be an object`
1239
- );
1240
- }
1241
-
1242
- const mode = (entry as { mode?: unknown }).mode;
1243
- const text = (entry as { text?: unknown }).text;
1244
- if (mode !== "term" && mode !== "intent" && mode !== "hyde") {
1245
- return errorResponse(
1246
- "VALIDATION",
1247
- `queryModes[${index}].mode must be one of: term, intent, hyde`
1248
- );
1249
- }
1250
- if (typeof text !== "string" || !text.trim()) {
1251
- return errorResponse(
1252
- "VALIDATION",
1253
- `queryModes[${index}].text must be a non-empty string`
1254
- );
1255
- }
1256
-
1257
- if (mode === "hyde") {
1258
- hydeCount += 1;
1259
- if (hydeCount > 1) {
1260
- return errorResponse(
1261
- "VALIDATION",
1262
- "Only one hyde mode is allowed in queryModes"
1263
- );
1264
- }
1265
- }
1266
-
1267
- queryModes.push({ mode, text: text.trim() });
1268
- }
1329
+ const { queryModes, error: queryModesError } = parseQueryModesInput(
1330
+ body.queryModes
1331
+ );
1332
+ if (queryModesError) {
1333
+ return queryModesError;
1269
1334
  }
1270
1335
 
1271
1336
  // Parse tag filters
@@ -1297,6 +1362,9 @@ export async function handleQuery(
1297
1362
  const categories = body.category
1298
1363
  ? parseCommaSeparatedValues(body.category)
1299
1364
  : undefined;
1365
+ const exclude = body.exclude
1366
+ ? parseCommaSeparatedValues(body.exclude)
1367
+ : undefined;
1300
1368
  const author = body.author?.trim() || undefined;
1301
1369
 
1302
1370
  const result = await searchHybrid(
@@ -1314,6 +1382,12 @@ export async function handleQuery(
1314
1382
  minScore: body.minScore,
1315
1383
  collection: body.collection,
1316
1384
  lang: body.lang,
1385
+ intent: body.intent?.trim() || undefined,
1386
+ candidateLimit:
1387
+ body.candidateLimit !== undefined
1388
+ ? Math.min(body.candidateLimit, 100)
1389
+ : undefined,
1390
+ exclude,
1317
1391
  queryModes,
1318
1392
  noExpand: body.noExpand,
1319
1393
  noRerank: body.noRerank,
@@ -1377,6 +1451,24 @@ export async function handleAsk(
1377
1451
  if (body.until !== undefined && typeof body.until !== "string") {
1378
1452
  return errorResponse("VALIDATION", "until must be a string");
1379
1453
  }
1454
+ if (body.intent !== undefined && typeof body.intent !== "string") {
1455
+ return errorResponse("VALIDATION", "intent must be a string");
1456
+ }
1457
+ if (body.exclude !== undefined && typeof body.exclude !== "string") {
1458
+ return errorResponse(
1459
+ "VALIDATION",
1460
+ "exclude must be a comma-separated string"
1461
+ );
1462
+ }
1463
+ if (
1464
+ body.candidateLimit !== undefined &&
1465
+ (typeof body.candidateLimit !== "number" || body.candidateLimit < 1)
1466
+ ) {
1467
+ return errorResponse(
1468
+ "VALIDATION",
1469
+ "candidateLimit must be a positive integer"
1470
+ );
1471
+ }
1380
1472
  if (body.category !== undefined && typeof body.category !== "string") {
1381
1473
  return errorResponse(
1382
1474
  "VALIDATION",
@@ -1387,6 +1479,13 @@ export async function handleAsk(
1387
1479
  return errorResponse("VALIDATION", "author must be a string");
1388
1480
  }
1389
1481
 
1482
+ const { queryModes, error: queryModesError } = parseQueryModesInput(
1483
+ body.queryModes
1484
+ );
1485
+ if (queryModesError) {
1486
+ return queryModesError;
1487
+ }
1488
+
1390
1489
  if (body.tagsAll) {
1391
1490
  try {
1392
1491
  tagsAll = parseAndValidateTagFilter(body.tagsAll);
@@ -1412,6 +1511,9 @@ export async function handleAsk(
1412
1511
  const categories = body.category
1413
1512
  ? parseCommaSeparatedValues(body.category)
1414
1513
  : undefined;
1514
+ const exclude = body.exclude
1515
+ ? parseCommaSeparatedValues(body.exclude)
1516
+ : undefined;
1415
1517
  const author = body.author?.trim() || undefined;
1416
1518
 
1417
1519
  const limit = Math.min(body.limit ?? 5, 20);
@@ -1431,8 +1533,15 @@ export async function handleAsk(
1431
1533
  limit,
1432
1534
  collection: body.collection,
1433
1535
  lang: body.lang,
1536
+ intent: body.intent?.trim() || undefined,
1434
1537
  noExpand: body.noExpand,
1435
1538
  noRerank: body.noRerank,
1539
+ candidateLimit:
1540
+ body.candidateLimit !== undefined
1541
+ ? Math.min(body.candidateLimit, 100)
1542
+ : undefined,
1543
+ exclude,
1544
+ queryModes,
1436
1545
  tagsAll,
1437
1546
  tagsAny,
1438
1547
  since: body.since,
@@ -1483,6 +1592,10 @@ export async function handleAsk(
1483
1592
  expanded: searchResult.value.meta.expanded ?? false,
1484
1593
  reranked: searchResult.value.meta.reranked ?? false,
1485
1594
  vectorsUsed: searchResult.value.meta.vectorsUsed ?? false,
1595
+ intent: searchResult.value.meta.intent,
1596
+ candidateLimit: searchResult.value.meta.candidateLimit,
1597
+ exclude: searchResult.value.meta.exclude,
1598
+ queryModes: searchResult.value.meta.queryModes,
1486
1599
  answerGenerated,
1487
1600
  totalResults: results.length,
1488
1601
  answerContext,