voyageai-cli 1.30.1 → 1.30.2
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.
- package/README.md +4 -4
- package/package.json +1 -1
- package/src/cli.js +2 -0
- package/src/commands/about.js +3 -3
- package/src/commands/code-search.js +751 -0
- package/src/commands/doctor.js +1 -1
- package/src/commands/index-workspace.js +9 -5
- package/src/commands/playground.js +9 -1
- package/src/commands/quickstart.js +4 -4
- package/src/commands/workflow.js +132 -65
- package/src/lib/catalog.js +4 -2
- package/src/lib/code-search.js +315 -0
- package/src/lib/codegen.js +1 -1
- package/src/lib/explanations.js +3 -3
- package/src/lib/github.js +226 -0
- package/src/lib/template-engine.js +154 -20
- package/src/lib/workflow-builder.js +753 -0
- package/src/lib/workflow-formatters.js +454 -0
- package/src/lib/workflow-input-cache.js +111 -0
- package/src/lib/workflow-scaffold.js +1 -1
- package/src/lib/workflow.js +91 -1
- package/src/mcp/schemas/index.js +130 -0
- package/src/mcp/server.js +17 -4
- package/src/mcp/tools/authoring.js +662 -0
- package/src/mcp/tools/code-search.js +620 -0
- package/src/mcp/tools/ingest.js +2 -5
- package/src/mcp/tools/retrieval.js +2 -15
- package/src/mcp/tools/workspace.js +1 -12
- package/src/mcp/utils.js +20 -0
- package/src/playground/help/workflow-nodes.js +127 -2
- package/src/playground/index.html +1366 -24
- package/src/workflows/code-review.json +110 -0
- package/src/workflows/cost-analysis.json +5 -0
- package/src/workflows/tests/code-review.fresh-index.test.json +83 -0
- package/src/workflows/tests/code-review.happy-path.test.json +121 -0
- package/src/workflows/tests/code-review.no-question.test.json +70 -0
- package/src/workflows/tests/smart-ingest.duplicate-detected.test.json +2 -2
|
@@ -1089,6 +1089,494 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
1089
1089
|
display: none;
|
|
1090
1090
|
}
|
|
1091
1091
|
|
|
1092
|
+
/* ── Models Tab ── */
|
|
1093
|
+
.models-toolbar {
|
|
1094
|
+
display: flex;
|
|
1095
|
+
align-items: center;
|
|
1096
|
+
gap: 12px;
|
|
1097
|
+
margin-bottom: 20px;
|
|
1098
|
+
flex-wrap: wrap;
|
|
1099
|
+
}
|
|
1100
|
+
.models-filter-pills {
|
|
1101
|
+
display: flex;
|
|
1102
|
+
gap: 6px;
|
|
1103
|
+
}
|
|
1104
|
+
.models-filter-pill {
|
|
1105
|
+
background: var(--bg-card);
|
|
1106
|
+
border: 1px solid var(--border);
|
|
1107
|
+
color: var(--text-dim);
|
|
1108
|
+
padding: 4px 12px;
|
|
1109
|
+
border-radius: 20px;
|
|
1110
|
+
font-size: 12px;
|
|
1111
|
+
font-family: var(--font);
|
|
1112
|
+
cursor: pointer;
|
|
1113
|
+
transition: all 0.15s;
|
|
1114
|
+
}
|
|
1115
|
+
.models-filter-pill:hover {
|
|
1116
|
+
border-color: var(--accent);
|
|
1117
|
+
color: var(--text);
|
|
1118
|
+
}
|
|
1119
|
+
.models-filter-pill.active {
|
|
1120
|
+
background: rgba(0, 212, 170, 0.12);
|
|
1121
|
+
border-color: var(--accent);
|
|
1122
|
+
color: var(--accent);
|
|
1123
|
+
}
|
|
1124
|
+
.models-hero {
|
|
1125
|
+
position: relative;
|
|
1126
|
+
overflow: hidden;
|
|
1127
|
+
background: linear-gradient(135deg, rgba(0, 212, 170, 0.06), rgba(64, 224, 255, 0.06));
|
|
1128
|
+
border: 1px solid rgba(0, 212, 170, 0.2);
|
|
1129
|
+
margin-bottom: 24px;
|
|
1130
|
+
}
|
|
1131
|
+
.models-hero-shape {
|
|
1132
|
+
position: absolute;
|
|
1133
|
+
top: -30px;
|
|
1134
|
+
right: -30px;
|
|
1135
|
+
width: 200px;
|
|
1136
|
+
height: 200px;
|
|
1137
|
+
pointer-events: none;
|
|
1138
|
+
z-index: 0;
|
|
1139
|
+
}
|
|
1140
|
+
.models-hero-content {
|
|
1141
|
+
position: relative;
|
|
1142
|
+
z-index: 1;
|
|
1143
|
+
}
|
|
1144
|
+
.models-hero-badge {
|
|
1145
|
+
display: inline-block;
|
|
1146
|
+
background: linear-gradient(135deg, #00D4AA, #40E0FF);
|
|
1147
|
+
color: #001E2B;
|
|
1148
|
+
font-size: 11px;
|
|
1149
|
+
font-weight: 700;
|
|
1150
|
+
padding: 3px 10px;
|
|
1151
|
+
border-radius: 12px;
|
|
1152
|
+
text-transform: uppercase;
|
|
1153
|
+
letter-spacing: 0.5px;
|
|
1154
|
+
margin-bottom: 10px;
|
|
1155
|
+
}
|
|
1156
|
+
.models-hero-title {
|
|
1157
|
+
font-size: 18px;
|
|
1158
|
+
font-weight: 700;
|
|
1159
|
+
color: var(--text);
|
|
1160
|
+
margin-bottom: 8px;
|
|
1161
|
+
}
|
|
1162
|
+
.models-hero-desc {
|
|
1163
|
+
font-size: 13px;
|
|
1164
|
+
color: var(--text-dim);
|
|
1165
|
+
line-height: 1.6;
|
|
1166
|
+
max-width: 700px;
|
|
1167
|
+
margin-bottom: 14px;
|
|
1168
|
+
}
|
|
1169
|
+
.models-hero-models {
|
|
1170
|
+
display: flex;
|
|
1171
|
+
gap: 8px;
|
|
1172
|
+
flex-wrap: wrap;
|
|
1173
|
+
}
|
|
1174
|
+
.model-chip {
|
|
1175
|
+
display: inline-flex;
|
|
1176
|
+
align-items: center;
|
|
1177
|
+
gap: 6px;
|
|
1178
|
+
padding: 6px 12px;
|
|
1179
|
+
background: var(--bg-card);
|
|
1180
|
+
border: 1px solid var(--border);
|
|
1181
|
+
border-radius: 8px;
|
|
1182
|
+
font-size: 12px;
|
|
1183
|
+
font-family: var(--mono);
|
|
1184
|
+
color: var(--text);
|
|
1185
|
+
cursor: pointer;
|
|
1186
|
+
transition: all 0.15s;
|
|
1187
|
+
}
|
|
1188
|
+
.model-chip:hover {
|
|
1189
|
+
border-color: var(--accent);
|
|
1190
|
+
background: rgba(0, 212, 170, 0.06);
|
|
1191
|
+
}
|
|
1192
|
+
.model-chip-price {
|
|
1193
|
+
color: var(--text-muted);
|
|
1194
|
+
font-size: 11px;
|
|
1195
|
+
}
|
|
1196
|
+
.models-group {
|
|
1197
|
+
margin-bottom: 24px;
|
|
1198
|
+
}
|
|
1199
|
+
.models-group-header {
|
|
1200
|
+
display: flex;
|
|
1201
|
+
align-items: center;
|
|
1202
|
+
gap: 8px;
|
|
1203
|
+
margin-bottom: 12px;
|
|
1204
|
+
}
|
|
1205
|
+
.models-group-icon {
|
|
1206
|
+
color: var(--accent);
|
|
1207
|
+
opacity: 0.8;
|
|
1208
|
+
}
|
|
1209
|
+
.models-group-title {
|
|
1210
|
+
font-size: 15px;
|
|
1211
|
+
font-weight: 600;
|
|
1212
|
+
color: var(--text);
|
|
1213
|
+
}
|
|
1214
|
+
.models-group-count {
|
|
1215
|
+
font-size: 11px;
|
|
1216
|
+
color: var(--text-muted);
|
|
1217
|
+
background: var(--bg-card);
|
|
1218
|
+
border: 1px solid var(--border);
|
|
1219
|
+
border-radius: 10px;
|
|
1220
|
+
padding: 1px 8px;
|
|
1221
|
+
}
|
|
1222
|
+
.models-group-grid {
|
|
1223
|
+
display: grid;
|
|
1224
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
1225
|
+
gap: 14px;
|
|
1226
|
+
}
|
|
1227
|
+
.model-card {
|
|
1228
|
+
background: var(--bg-card);
|
|
1229
|
+
border: 1px solid var(--border);
|
|
1230
|
+
border-radius: var(--radius);
|
|
1231
|
+
padding: 18px;
|
|
1232
|
+
cursor: default;
|
|
1233
|
+
transition: all 0.2s;
|
|
1234
|
+
position: relative;
|
|
1235
|
+
overflow: hidden;
|
|
1236
|
+
}
|
|
1237
|
+
.model-card:hover {
|
|
1238
|
+
border-color: var(--accent);
|
|
1239
|
+
transform: translateY(-2px);
|
|
1240
|
+
box-shadow: 0 4px 20px rgba(0, 212, 170, 0.1);
|
|
1241
|
+
}
|
|
1242
|
+
.model-card.legacy {
|
|
1243
|
+
opacity: 0.5;
|
|
1244
|
+
}
|
|
1245
|
+
.model-card.legacy:hover {
|
|
1246
|
+
opacity: 0.7;
|
|
1247
|
+
}
|
|
1248
|
+
.model-card-nebula {
|
|
1249
|
+
position: absolute;
|
|
1250
|
+
top: -40px;
|
|
1251
|
+
right: -40px;
|
|
1252
|
+
width: 140px;
|
|
1253
|
+
height: 140px;
|
|
1254
|
+
pointer-events: none;
|
|
1255
|
+
z-index: 0;
|
|
1256
|
+
}
|
|
1257
|
+
.model-card-header {
|
|
1258
|
+
display: flex;
|
|
1259
|
+
align-items: center;
|
|
1260
|
+
justify-content: space-between;
|
|
1261
|
+
margin-bottom: 6px;
|
|
1262
|
+
position: relative;
|
|
1263
|
+
z-index: 1;
|
|
1264
|
+
}
|
|
1265
|
+
.model-card-name {
|
|
1266
|
+
font-size: 15px;
|
|
1267
|
+
font-weight: 600;
|
|
1268
|
+
color: var(--text);
|
|
1269
|
+
font-family: var(--mono);
|
|
1270
|
+
}
|
|
1271
|
+
.model-card-type-badge {
|
|
1272
|
+
font-size: 10px;
|
|
1273
|
+
padding: 2px 8px;
|
|
1274
|
+
border-radius: 10px;
|
|
1275
|
+
text-transform: uppercase;
|
|
1276
|
+
letter-spacing: 0.3px;
|
|
1277
|
+
font-weight: 600;
|
|
1278
|
+
}
|
|
1279
|
+
.model-card-type-badge.embedding {
|
|
1280
|
+
background: rgba(0, 212, 170, 0.15);
|
|
1281
|
+
color: #00D4AA;
|
|
1282
|
+
}
|
|
1283
|
+
.model-card-type-badge.reranking {
|
|
1284
|
+
background: rgba(64, 224, 255, 0.15);
|
|
1285
|
+
color: #40E0FF;
|
|
1286
|
+
}
|
|
1287
|
+
.model-card-type-badge.multimodal {
|
|
1288
|
+
background: rgba(255, 170, 64, 0.15);
|
|
1289
|
+
color: #FFAA40;
|
|
1290
|
+
}
|
|
1291
|
+
.model-card-shared-badge {
|
|
1292
|
+
display: inline-flex;
|
|
1293
|
+
align-items: center;
|
|
1294
|
+
gap: 4px;
|
|
1295
|
+
font-size: 10px;
|
|
1296
|
+
color: var(--blue, #40E0FF);
|
|
1297
|
+
margin-bottom: 6px;
|
|
1298
|
+
position: relative;
|
|
1299
|
+
z-index: 1;
|
|
1300
|
+
}
|
|
1301
|
+
.model-card-rteb {
|
|
1302
|
+
position: absolute;
|
|
1303
|
+
top: 14px;
|
|
1304
|
+
right: 14px;
|
|
1305
|
+
font-size: 11px;
|
|
1306
|
+
font-weight: 700;
|
|
1307
|
+
color: var(--accent);
|
|
1308
|
+
font-family: var(--mono);
|
|
1309
|
+
z-index: 1;
|
|
1310
|
+
}
|
|
1311
|
+
.model-card-desc {
|
|
1312
|
+
font-size: 13px;
|
|
1313
|
+
color: var(--text-dim);
|
|
1314
|
+
margin-bottom: 12px;
|
|
1315
|
+
position: relative;
|
|
1316
|
+
z-index: 1;
|
|
1317
|
+
}
|
|
1318
|
+
.model-card-specs {
|
|
1319
|
+
display: grid;
|
|
1320
|
+
grid-template-columns: 1fr 1fr;
|
|
1321
|
+
gap: 6px 16px;
|
|
1322
|
+
font-size: 12px;
|
|
1323
|
+
position: relative;
|
|
1324
|
+
z-index: 1;
|
|
1325
|
+
}
|
|
1326
|
+
.model-card-spec {
|
|
1327
|
+
display: flex;
|
|
1328
|
+
justify-content: space-between;
|
|
1329
|
+
}
|
|
1330
|
+
.model-card-spec-label {
|
|
1331
|
+
color: var(--text-muted);
|
|
1332
|
+
}
|
|
1333
|
+
.model-card-spec-value {
|
|
1334
|
+
color: var(--text);
|
|
1335
|
+
font-family: var(--mono);
|
|
1336
|
+
font-weight: 500;
|
|
1337
|
+
}
|
|
1338
|
+
.model-card-actions {
|
|
1339
|
+
display: flex;
|
|
1340
|
+
gap: 6px;
|
|
1341
|
+
margin-top: 12px;
|
|
1342
|
+
position: relative;
|
|
1343
|
+
z-index: 1;
|
|
1344
|
+
}
|
|
1345
|
+
.model-card-action {
|
|
1346
|
+
font-size: 11px;
|
|
1347
|
+
padding: 4px 10px;
|
|
1348
|
+
border-radius: 6px;
|
|
1349
|
+
border: 1px solid var(--border);
|
|
1350
|
+
background: transparent;
|
|
1351
|
+
color: var(--accent);
|
|
1352
|
+
cursor: pointer;
|
|
1353
|
+
font-family: var(--font);
|
|
1354
|
+
transition: all 0.15s;
|
|
1355
|
+
}
|
|
1356
|
+
.model-card-action:hover {
|
|
1357
|
+
background: rgba(0, 212, 170, 0.1);
|
|
1358
|
+
border-color: var(--accent);
|
|
1359
|
+
}
|
|
1360
|
+
.models-rteb-bar {
|
|
1361
|
+
display: flex;
|
|
1362
|
+
align-items: center;
|
|
1363
|
+
gap: 12px;
|
|
1364
|
+
margin-bottom: 8px;
|
|
1365
|
+
}
|
|
1366
|
+
.models-rteb-label {
|
|
1367
|
+
min-width: 160px;
|
|
1368
|
+
font-size: 13px;
|
|
1369
|
+
white-space: nowrap;
|
|
1370
|
+
}
|
|
1371
|
+
.models-rteb-track {
|
|
1372
|
+
flex: 1;
|
|
1373
|
+
height: 24px;
|
|
1374
|
+
background: var(--bg-surface);
|
|
1375
|
+
border-radius: 4px;
|
|
1376
|
+
overflow: hidden;
|
|
1377
|
+
}
|
|
1378
|
+
.models-rteb-fill {
|
|
1379
|
+
height: 100%;
|
|
1380
|
+
border-radius: 4px;
|
|
1381
|
+
transition: width 0.6s ease-out;
|
|
1382
|
+
}
|
|
1383
|
+
.models-rteb-score {
|
|
1384
|
+
min-width: 50px;
|
|
1385
|
+
text-align: right;
|
|
1386
|
+
font-family: var(--mono);
|
|
1387
|
+
font-weight: 600;
|
|
1388
|
+
font-size: 13px;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/* ── Model Picker Overlay ── */
|
|
1392
|
+
.model-picker-overlay {
|
|
1393
|
+
position: fixed;
|
|
1394
|
+
inset: 0;
|
|
1395
|
+
background: rgba(0, 0, 0, 0.6);
|
|
1396
|
+
backdrop-filter: blur(4px);
|
|
1397
|
+
z-index: 1001;
|
|
1398
|
+
display: flex;
|
|
1399
|
+
align-items: center;
|
|
1400
|
+
justify-content: center;
|
|
1401
|
+
opacity: 0;
|
|
1402
|
+
pointer-events: none;
|
|
1403
|
+
transition: opacity 0.2s ease;
|
|
1404
|
+
}
|
|
1405
|
+
.model-picker-overlay.open {
|
|
1406
|
+
opacity: 1;
|
|
1407
|
+
pointer-events: auto;
|
|
1408
|
+
}
|
|
1409
|
+
.model-picker {
|
|
1410
|
+
background: var(--bg-card);
|
|
1411
|
+
border: 1px solid var(--border);
|
|
1412
|
+
border-radius: 12px;
|
|
1413
|
+
width: 480px;
|
|
1414
|
+
max-width: 90vw;
|
|
1415
|
+
max-height: 70vh;
|
|
1416
|
+
display: flex;
|
|
1417
|
+
flex-direction: column;
|
|
1418
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
1419
|
+
}
|
|
1420
|
+
.model-picker-header {
|
|
1421
|
+
display: flex;
|
|
1422
|
+
align-items: center;
|
|
1423
|
+
justify-content: space-between;
|
|
1424
|
+
padding: 16px 20px 12px;
|
|
1425
|
+
border-bottom: 1px solid var(--border);
|
|
1426
|
+
}
|
|
1427
|
+
.model-picker-title {
|
|
1428
|
+
font-size: 16px;
|
|
1429
|
+
font-weight: 600;
|
|
1430
|
+
color: var(--text);
|
|
1431
|
+
}
|
|
1432
|
+
.model-picker-context {
|
|
1433
|
+
font-size: 12px;
|
|
1434
|
+
color: var(--text-muted);
|
|
1435
|
+
margin-top: 2px;
|
|
1436
|
+
}
|
|
1437
|
+
.model-picker-close {
|
|
1438
|
+
background: none;
|
|
1439
|
+
border: none;
|
|
1440
|
+
color: var(--text-dim);
|
|
1441
|
+
font-size: 22px;
|
|
1442
|
+
cursor: pointer;
|
|
1443
|
+
padding: 0 4px;
|
|
1444
|
+
line-height: 1;
|
|
1445
|
+
}
|
|
1446
|
+
.model-picker-close:hover {
|
|
1447
|
+
color: var(--text);
|
|
1448
|
+
}
|
|
1449
|
+
.model-picker-search {
|
|
1450
|
+
padding: 12px 20px;
|
|
1451
|
+
}
|
|
1452
|
+
.model-picker-search input {
|
|
1453
|
+
width: 100%;
|
|
1454
|
+
background: var(--bg-input, var(--bg-surface));
|
|
1455
|
+
border: 1px solid var(--border);
|
|
1456
|
+
color: var(--text);
|
|
1457
|
+
padding: 8px 12px;
|
|
1458
|
+
border-radius: var(--radius);
|
|
1459
|
+
font-size: 13px;
|
|
1460
|
+
font-family: var(--font);
|
|
1461
|
+
}
|
|
1462
|
+
.model-picker-recommendation {
|
|
1463
|
+
padding: 0 20px 8px;
|
|
1464
|
+
font-size: 12px;
|
|
1465
|
+
color: var(--blue, #40E0FF);
|
|
1466
|
+
display: none;
|
|
1467
|
+
}
|
|
1468
|
+
.model-picker-recommendation.visible {
|
|
1469
|
+
display: block;
|
|
1470
|
+
}
|
|
1471
|
+
.model-picker-list {
|
|
1472
|
+
flex: 1;
|
|
1473
|
+
overflow-y: auto;
|
|
1474
|
+
padding: 0 12px 12px;
|
|
1475
|
+
}
|
|
1476
|
+
.model-picker-item {
|
|
1477
|
+
display: flex;
|
|
1478
|
+
align-items: flex-start;
|
|
1479
|
+
gap: 12px;
|
|
1480
|
+
padding: 10px 12px;
|
|
1481
|
+
border-radius: 8px;
|
|
1482
|
+
cursor: pointer;
|
|
1483
|
+
transition: background 0.1s;
|
|
1484
|
+
border: 1px solid transparent;
|
|
1485
|
+
}
|
|
1486
|
+
.model-picker-item:hover,
|
|
1487
|
+
.model-picker-item:focus {
|
|
1488
|
+
background: rgba(0, 212, 170, 0.06);
|
|
1489
|
+
border-color: var(--border);
|
|
1490
|
+
outline: none;
|
|
1491
|
+
}
|
|
1492
|
+
.model-picker-item.selected {
|
|
1493
|
+
background: rgba(0, 212, 170, 0.1);
|
|
1494
|
+
border-color: var(--accent);
|
|
1495
|
+
}
|
|
1496
|
+
.model-picker-item-radio {
|
|
1497
|
+
width: 16px;
|
|
1498
|
+
height: 16px;
|
|
1499
|
+
border: 2px solid var(--border);
|
|
1500
|
+
border-radius: 50%;
|
|
1501
|
+
flex-shrink: 0;
|
|
1502
|
+
margin-top: 2px;
|
|
1503
|
+
transition: all 0.15s;
|
|
1504
|
+
}
|
|
1505
|
+
.model-picker-item.selected .model-picker-item-radio {
|
|
1506
|
+
border-color: var(--accent);
|
|
1507
|
+
background: var(--accent);
|
|
1508
|
+
box-shadow: inset 0 0 0 3px var(--bg-card);
|
|
1509
|
+
}
|
|
1510
|
+
.model-picker-item-info {
|
|
1511
|
+
flex: 1;
|
|
1512
|
+
min-width: 0;
|
|
1513
|
+
}
|
|
1514
|
+
.model-picker-item-name {
|
|
1515
|
+
font-size: 13px;
|
|
1516
|
+
font-weight: 600;
|
|
1517
|
+
color: var(--text);
|
|
1518
|
+
font-family: var(--mono);
|
|
1519
|
+
}
|
|
1520
|
+
.model-picker-item-meta {
|
|
1521
|
+
font-size: 11px;
|
|
1522
|
+
color: var(--text-dim);
|
|
1523
|
+
margin-top: 2px;
|
|
1524
|
+
}
|
|
1525
|
+
.model-picker-item-tags {
|
|
1526
|
+
display: flex;
|
|
1527
|
+
gap: 4px;
|
|
1528
|
+
margin-top: 4px;
|
|
1529
|
+
flex-wrap: wrap;
|
|
1530
|
+
}
|
|
1531
|
+
.model-picker-item-tag {
|
|
1532
|
+
font-size: 10px;
|
|
1533
|
+
padding: 1px 6px;
|
|
1534
|
+
border-radius: 8px;
|
|
1535
|
+
background: rgba(0, 212, 170, 0.08);
|
|
1536
|
+
color: var(--accent);
|
|
1537
|
+
}
|
|
1538
|
+
.model-picker-item-shared {
|
|
1539
|
+
font-size: 10px;
|
|
1540
|
+
padding: 1px 6px;
|
|
1541
|
+
border-radius: 8px;
|
|
1542
|
+
background: rgba(64, 224, 255, 0.1);
|
|
1543
|
+
color: var(--blue, #40E0FF);
|
|
1544
|
+
}
|
|
1545
|
+
.model-picker-footer {
|
|
1546
|
+
padding: 10px 20px;
|
|
1547
|
+
border-top: 1px solid var(--border);
|
|
1548
|
+
text-align: center;
|
|
1549
|
+
font-size: 12px;
|
|
1550
|
+
}
|
|
1551
|
+
.model-picker-footer a {
|
|
1552
|
+
color: var(--accent);
|
|
1553
|
+
text-decoration: none;
|
|
1554
|
+
}
|
|
1555
|
+
.model-picker-footer a:hover {
|
|
1556
|
+
text-decoration: underline;
|
|
1557
|
+
}
|
|
1558
|
+
.model-picker-trigger {
|
|
1559
|
+
background: transparent;
|
|
1560
|
+
border: 1px solid var(--border);
|
|
1561
|
+
color: var(--text-muted);
|
|
1562
|
+
cursor: pointer;
|
|
1563
|
+
padding: 4px 6px;
|
|
1564
|
+
border-radius: 4px;
|
|
1565
|
+
transition: color 0.15s, background 0.15s, border-color 0.15s;
|
|
1566
|
+
display: inline-flex;
|
|
1567
|
+
align-items: center;
|
|
1568
|
+
vertical-align: middle;
|
|
1569
|
+
margin-left: 4px;
|
|
1570
|
+
}
|
|
1571
|
+
.model-picker-trigger:hover {
|
|
1572
|
+
color: var(--accent);
|
|
1573
|
+
background: rgba(0, 212, 170, 0.08);
|
|
1574
|
+
border-color: var(--accent);
|
|
1575
|
+
}
|
|
1576
|
+
[data-theme="light"] .model-card:hover {
|
|
1577
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1092
1580
|
/* Explore modal */
|
|
1093
1581
|
.explore-modal-overlay {
|
|
1094
1582
|
position: fixed;
|
|
@@ -4200,6 +4688,54 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
4200
4688
|
border-radius: 6px; padding: 8px; overflow-x: auto;
|
|
4201
4689
|
white-space: pre-wrap; color: var(--text); margin-top: 4px;
|
|
4202
4690
|
}
|
|
4691
|
+
/* Output format selector and rich rendering */
|
|
4692
|
+
.wf-output-format-select {
|
|
4693
|
+
font-size: 11px; padding: 2px 6px; border-radius: 4px;
|
|
4694
|
+
background: var(--bg-card); border: 1px solid var(--border);
|
|
4695
|
+
color: var(--text); cursor: pointer; margin-left: 8px;
|
|
4696
|
+
}
|
|
4697
|
+
.wf-output-format-select:hover { border-color: var(--accent); }
|
|
4698
|
+
.wf-output-table {
|
|
4699
|
+
width: 100%; border-collapse: collapse; font-size: 11px;
|
|
4700
|
+
color: var(--text); margin-top: 8px;
|
|
4701
|
+
}
|
|
4702
|
+
.wf-output-table th {
|
|
4703
|
+
text-align: left; padding: 6px 8px; font-weight: 600;
|
|
4704
|
+
border-bottom: 1px solid var(--accent); color: var(--accent);
|
|
4705
|
+
font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
|
|
4706
|
+
}
|
|
4707
|
+
.wf-output-table td {
|
|
4708
|
+
padding: 5px 8px; border-bottom: 1px solid var(--border);
|
|
4709
|
+
font-family: 'SF Mono', 'Fira Code', monospace; font-size: 11px;
|
|
4710
|
+
}
|
|
4711
|
+
.wf-output-table tr:hover td { background: rgba(0,212,170,0.05); }
|
|
4712
|
+
.wf-output-markdown {
|
|
4713
|
+
font-size: 12px; line-height: 1.5; color: var(--text); padding: 8px;
|
|
4714
|
+
}
|
|
4715
|
+
.wf-output-markdown h2, .wf-output-markdown h3 {
|
|
4716
|
+
color: var(--accent); margin-top: 12px; margin-bottom: 4px;
|
|
4717
|
+
}
|
|
4718
|
+
.wf-output-markdown table {
|
|
4719
|
+
border-collapse: collapse; width: 100%; margin: 8px 0;
|
|
4720
|
+
}
|
|
4721
|
+
.wf-output-markdown th, .wf-output-markdown td {
|
|
4722
|
+
border: 1px solid var(--border); padding: 4px 8px; font-size: 11px;
|
|
4723
|
+
}
|
|
4724
|
+
.wf-output-text-field { margin-bottom: 12px; }
|
|
4725
|
+
.wf-output-text-label {
|
|
4726
|
+
font-size: 10px; font-weight: 600; text-transform: uppercase;
|
|
4727
|
+
color: var(--accent); letter-spacing: 0.5px; margin-bottom: 2px;
|
|
4728
|
+
}
|
|
4729
|
+
.wf-output-text-value {
|
|
4730
|
+
font-size: 12px; color: var(--text); line-height: 1.5; white-space: pre-wrap;
|
|
4731
|
+
}
|
|
4732
|
+
.wf-output-metric {
|
|
4733
|
+
display: inline-block; margin-right: 16px; margin-bottom: 6px;
|
|
4734
|
+
}
|
|
4735
|
+
.wf-output-metric-val {
|
|
4736
|
+
font-family: 'SF Mono', 'Fira Code', monospace; font-size: 13px;
|
|
4737
|
+
color: #40E0FF; font-weight: 600;
|
|
4738
|
+
}
|
|
4203
4739
|
.wf-tool-badge {
|
|
4204
4740
|
display: inline-flex; align-items: center; gap: 4px;
|
|
4205
4741
|
padding: 2px 8px; border-radius: 10px; font-size: 11px;
|
|
@@ -5754,6 +6290,7 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
5754
6290
|
<div class="sidebar-nav-divider"></div>
|
|
5755
6291
|
<div class="sidebar-nav-group" role="tablist" aria-label="Learn">
|
|
5756
6292
|
<div class="sidebar-nav-label" id="nav-learn-label">Learn</div>
|
|
6293
|
+
<button class="tab-btn" data-tab="models" data-tooltip="Models" role="tab" aria-selected="false" aria-controls="tab-models" id="tab-btn-models"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-bot"/></svg></span><span>Models</span></button>
|
|
5757
6294
|
<button class="tab-btn" data-tab="benchmark" data-tooltip="Benchmark" role="tab" aria-selected="false" aria-controls="tab-benchmark" id="tab-btn-benchmark"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-gauge"/></svg></span><span>Benchmark</span></button>
|
|
5758
6295
|
<button class="tab-btn" data-tab="explore" data-tooltip="Explore" role="tab" aria-selected="false" aria-controls="tab-explore" id="tab-btn-explore"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-bulb"/></svg></span><span>Explore</span></button>
|
|
5759
6296
|
<button class="tab-btn" data-tab="about" data-tooltip="About" role="tab" aria-selected="false" aria-controls="tab-about" id="tab-btn-about"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-info"/></svg></span><span>About</span></button>
|
|
@@ -6474,9 +7011,9 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
6474
7011
|
<div style="margin-top:16px;padding:12px;background:var(--accent-glow);border-radius:8px;border-left:3px solid var(--accent);">
|
|
6475
7012
|
<strong style="color:var(--accent);">💡 Pro Tip: Asymmetric Retrieval</strong>
|
|
6476
7013
|
<p style="margin:8px 0 0 0;font-size:13px;color:var(--text);">
|
|
6477
|
-
Embed your document corpus with <code style="background:var(--surface);padding:2px 6px;border-radius:4px;">voyage-
|
|
6478
|
-
query with <code style="background:var(--surface);padding:2px 6px;border-radius:4px;">voyage-
|
|
6479
|
-
Because all Voyage
|
|
7014
|
+
Embed your document corpus with <code style="background:var(--surface);padding:2px 6px;border-radius:4px;">voyage-4-lite</code> ($0.02/M) and
|
|
7015
|
+
query with <code style="background:var(--surface);padding:2px 6px;border-radius:4px;">voyage-4</code> ($0.06/M).
|
|
7016
|
+
Because all Voyage 4 models share the same embedding space, you get top-tier retrieval quality at a fraction of the cost.
|
|
6480
7017
|
</p>
|
|
6481
7018
|
</div>
|
|
6482
7019
|
</div>
|
|
@@ -6489,8 +7026,8 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
6489
7026
|
<div style="font-size:20px;margin-bottom:8px;">🔗</div>
|
|
6490
7027
|
<div style="font-weight:600;margin-bottom:4px;">Shared Embedding Space</div>
|
|
6491
7028
|
<div style="font-size:13px;color:var(--text-dim);">
|
|
6492
|
-
All Voyage
|
|
6493
|
-
with <code>voyage-
|
|
7029
|
+
All Voyage 4 models produce compatible embeddings. Mix <code>voyage-4-large</code> for queries
|
|
7030
|
+
with <code>voyage-4-lite</code> for documents, and they work together seamlessly.
|
|
6494
7031
|
</div>
|
|
6495
7032
|
</div>
|
|
6496
7033
|
<div style="padding:16px;background:var(--surface);border-radius:8px;border:1px solid var(--border);">
|
|
@@ -7112,7 +7649,7 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
7112
7649
|
<option value="voyage-4-large">voyage-4-large</option>
|
|
7113
7650
|
<option value="voyage-4">voyage-4</option>
|
|
7114
7651
|
<option value="voyage-4-lite">voyage-4-lite</option>
|
|
7115
|
-
<option value="voyage-3
|
|
7652
|
+
<option value="voyage-code-3">voyage-code-3</option>
|
|
7116
7653
|
</select>
|
|
7117
7654
|
</label>
|
|
7118
7655
|
<label>
|
|
@@ -7233,6 +7770,54 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
7233
7770
|
</div>
|
|
7234
7771
|
</div>
|
|
7235
7772
|
|
|
7773
|
+
<!-- ========== MODELS TAB ========== -->
|
|
7774
|
+
<div class="tab-panel" id="tab-models" role="tabpanel" aria-labelledby="tab-btn-models" tabindex="0">
|
|
7775
|
+
<div class="page-header">
|
|
7776
|
+
<h2 class="page-header-title">Models</h2>
|
|
7777
|
+
<p class="page-header-subtitle">Voyage AI model showcase</p>
|
|
7778
|
+
<p class="page-header-hint">Explore all Voyage AI models, compare specs, and find the right model for your use case.</p>
|
|
7779
|
+
<a class="page-header-docs" href="https://docs.voyageai.com/docs/embeddings" target="_blank" rel="noopener" title="Voyage AI model docs"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 1h7l3 3v11H3z"/><path d="M10 1v3h3"/></svg>Docs</a>
|
|
7780
|
+
</div>
|
|
7781
|
+
|
|
7782
|
+
<!-- Search + Filter -->
|
|
7783
|
+
<div class="models-toolbar">
|
|
7784
|
+
<input type="text" id="modelsSearch" placeholder="Search models..." oninput="filterModels()" style="max-width:280px;" aria-label="Search models">
|
|
7785
|
+
<div class="models-filter-pills" id="modelsFilterPills">
|
|
7786
|
+
<button class="models-filter-pill active" data-filter="all" onclick="setModelFilter('all')">All</button>
|
|
7787
|
+
<button class="models-filter-pill" data-filter="embedding" onclick="setModelFilter('embedding')">Embedding</button>
|
|
7788
|
+
<button class="models-filter-pill" data-filter="reranking" onclick="setModelFilter('reranking')">Reranking</button>
|
|
7789
|
+
<button class="models-filter-pill" data-filter="multimodal" onclick="setModelFilter('multimodal')">Multimodal</button>
|
|
7790
|
+
</div>
|
|
7791
|
+
</div>
|
|
7792
|
+
|
|
7793
|
+
<!-- Shared Embedding Space Hero -->
|
|
7794
|
+
<div class="models-hero card" id="modelsHero">
|
|
7795
|
+
<div class="models-hero-shape" id="modelsHeroShape"></div>
|
|
7796
|
+
<div class="models-hero-content">
|
|
7797
|
+
<div class="models-hero-badge">Voyage 4 Family</div>
|
|
7798
|
+
<div class="models-hero-title">Shared Embedding Space</div>
|
|
7799
|
+
<div class="models-hero-desc">
|
|
7800
|
+
voyage-4-large, voyage-4, and voyage-4-lite produce compatible embeddings in the same vector space.
|
|
7801
|
+
Index documents with the large model for best quality, then query with the lite model at a fraction of the cost.
|
|
7802
|
+
No re-indexing required.
|
|
7803
|
+
</div>
|
|
7804
|
+
<div class="models-hero-models" id="modelsHeroModels"></div>
|
|
7805
|
+
</div>
|
|
7806
|
+
</div>
|
|
7807
|
+
|
|
7808
|
+
<!-- Model Groups (dynamically rendered) -->
|
|
7809
|
+
<div id="modelsGroups"></div>
|
|
7810
|
+
|
|
7811
|
+
<!-- RTEB Benchmark Chart -->
|
|
7812
|
+
<div class="card" id="modelsRtebCard" style="margin-top:24px;">
|
|
7813
|
+
<div class="card-title">RTEB Retrieval Benchmark (NDCG@10)</div>
|
|
7814
|
+
<p style="color:var(--text-dim);font-size:13px;margin-bottom:16px;">
|
|
7815
|
+
Industry-standard benchmark for retrieval quality across 29 datasets. Higher is better.
|
|
7816
|
+
</p>
|
|
7817
|
+
<div id="modelsRtebChart"></div>
|
|
7818
|
+
</div>
|
|
7819
|
+
</div>
|
|
7820
|
+
|
|
7236
7821
|
<!-- ========== EXPLORE TAB ========== -->
|
|
7237
7822
|
<div class="tab-panel" id="tab-explore" role="tabpanel" aria-labelledby="tab-btn-explore" tabindex="0">
|
|
7238
7823
|
<div class="page-header">
|
|
@@ -7266,6 +7851,27 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
7266
7851
|
<!-- Hidden global model select (used by JS for model sync) -->
|
|
7267
7852
|
<select id="globalModel" style="display:none;"></select>
|
|
7268
7853
|
|
|
7854
|
+
<!-- ========== MODEL PICKER OVERLAY ========== -->
|
|
7855
|
+
<div class="model-picker-overlay" id="modelPickerOverlay" role="dialog" aria-modal="true" aria-labelledby="modelPickerTitle">
|
|
7856
|
+
<div class="model-picker">
|
|
7857
|
+
<div class="model-picker-header">
|
|
7858
|
+
<div>
|
|
7859
|
+
<div class="model-picker-title" id="modelPickerTitle">Choose a Model</div>
|
|
7860
|
+
<div class="model-picker-context" id="modelPickerContext"></div>
|
|
7861
|
+
</div>
|
|
7862
|
+
<button class="model-picker-close" id="modelPickerClose" aria-label="Close">×</button>
|
|
7863
|
+
</div>
|
|
7864
|
+
<div class="model-picker-search">
|
|
7865
|
+
<input type="text" id="modelPickerSearch" placeholder="Search models..." oninput="filterModelPicker()" aria-label="Search models">
|
|
7866
|
+
</div>
|
|
7867
|
+
<div class="model-picker-recommendation" id="modelPickerRecommendation"></div>
|
|
7868
|
+
<div class="model-picker-list" id="modelPickerList" role="listbox"></div>
|
|
7869
|
+
<div class="model-picker-footer">
|
|
7870
|
+
<a href="#" onclick="event.preventDefault(); closeModelPicker(); switchTab('models');">View all models in catalog</a>
|
|
7871
|
+
</div>
|
|
7872
|
+
</div>
|
|
7873
|
+
</div>
|
|
7874
|
+
|
|
7269
7875
|
<!-- ========== SETTINGS TAB ========== -->
|
|
7270
7876
|
<div class="tab-panel" id="tab-settings">
|
|
7271
7877
|
<div class="settings-layout">
|
|
@@ -8063,6 +8669,7 @@ async function init() {
|
|
|
8063
8669
|
await loadConfig();
|
|
8064
8670
|
await Promise.all([loadModels(), loadConcepts()]);
|
|
8065
8671
|
populateModelSelects();
|
|
8672
|
+
enhanceModelSelects();
|
|
8066
8673
|
buildExploreCards();
|
|
8067
8674
|
|
|
8068
8675
|
// Apply default tab setting
|
|
@@ -8146,6 +8753,9 @@ function switchTab(tab) {
|
|
|
8146
8753
|
if (tab === 'home') {
|
|
8147
8754
|
homeInit();
|
|
8148
8755
|
}
|
|
8756
|
+
if (tab === 'models') {
|
|
8757
|
+
modelsInit();
|
|
8758
|
+
}
|
|
8149
8759
|
}
|
|
8150
8760
|
// Expose globally so Electron main process can call it
|
|
8151
8761
|
window.switchTab = switchTab;
|
|
@@ -9541,6 +10151,393 @@ window.filterExplore = function() {
|
|
|
9541
10151
|
});
|
|
9542
10152
|
};
|
|
9543
10153
|
|
|
10154
|
+
// ── Models Tab ──
|
|
10155
|
+
let modelsTabData = {
|
|
10156
|
+
catalog: null,
|
|
10157
|
+
benchmarks: null,
|
|
10158
|
+
currentFilter: 'all',
|
|
10159
|
+
initialized: false
|
|
10160
|
+
};
|
|
10161
|
+
|
|
10162
|
+
async function loadModelsCatalog() {
|
|
10163
|
+
if (modelsTabData.catalog) return;
|
|
10164
|
+
try {
|
|
10165
|
+
const res = await fetch('/api/models/catalog');
|
|
10166
|
+
const data = await res.json();
|
|
10167
|
+
modelsTabData.catalog = data.models || [];
|
|
10168
|
+
modelsTabData.benchmarks = data.benchmarks || [];
|
|
10169
|
+
} catch {
|
|
10170
|
+
modelsTabData.catalog = allModels;
|
|
10171
|
+
modelsTabData.benchmarks = [];
|
|
10172
|
+
}
|
|
10173
|
+
}
|
|
10174
|
+
|
|
10175
|
+
function modelsInit() {
|
|
10176
|
+
if (modelsTabData.initialized) return;
|
|
10177
|
+
modelsTabData.initialized = true;
|
|
10178
|
+
loadModelsCatalog().then(() => {
|
|
10179
|
+
renderModelsHero();
|
|
10180
|
+
renderModelsGroups();
|
|
10181
|
+
renderModelsRtebChart();
|
|
10182
|
+
});
|
|
10183
|
+
}
|
|
10184
|
+
|
|
10185
|
+
function renderModelsHero() {
|
|
10186
|
+
const heroModels = document.getElementById('modelsHeroModels');
|
|
10187
|
+
const sharedModels = (modelsTabData.catalog || []).filter(
|
|
10188
|
+
m => m.sharedSpace === 'voyage-4' && !m.legacy && !m.unreleased
|
|
10189
|
+
);
|
|
10190
|
+
heroModels.innerHTML = sharedModels.map(m =>
|
|
10191
|
+
'<div class="model-chip" onclick="scrollToModelCard(\'' + m.name + '\')">' +
|
|
10192
|
+
'<span>' + escapeHtml(m.name) + '</span>' +
|
|
10193
|
+
'<span class="model-chip-price">' + escapeHtml(m.price) + '</span>' +
|
|
10194
|
+
'</div>'
|
|
10195
|
+
).join('');
|
|
10196
|
+
|
|
10197
|
+
const shapeEl = document.getElementById('modelsHeroShape');
|
|
10198
|
+
if (shapeEl && !shapeEl.innerHTML) {
|
|
10199
|
+
shapeEl.innerHTML = generateNebulaSVG(200, 200, 900, {variant: 'rich', opacity: 0.08});
|
|
10200
|
+
}
|
|
10201
|
+
}
|
|
10202
|
+
|
|
10203
|
+
function getModelGroups(models) {
|
|
10204
|
+
const groups = [
|
|
10205
|
+
{
|
|
10206
|
+
id: 'voyage-4',
|
|
10207
|
+
title: 'Voyage 4 Flagship',
|
|
10208
|
+
icon: LI.zap,
|
|
10209
|
+
models: models.filter(m => m.family === 'voyage-4' && !m.legacy)
|
|
10210
|
+
},
|
|
10211
|
+
{
|
|
10212
|
+
id: 'domain',
|
|
10213
|
+
title: 'Domain-Specific',
|
|
10214
|
+
icon: LI.target,
|
|
10215
|
+
models: models.filter(m => !m.family && m.type === 'embedding' && !m.legacy && !m.multimodal)
|
|
10216
|
+
},
|
|
10217
|
+
{
|
|
10218
|
+
id: 'multimodal',
|
|
10219
|
+
title: 'Multimodal',
|
|
10220
|
+
icon: LI.image,
|
|
10221
|
+
models: models.filter(m => m.multimodal && !m.legacy)
|
|
10222
|
+
},
|
|
10223
|
+
{
|
|
10224
|
+
id: 'reranking',
|
|
10225
|
+
title: 'Reranking',
|
|
10226
|
+
icon: LI.shuffle,
|
|
10227
|
+
models: models.filter(m => m.type === 'reranking' && !m.legacy)
|
|
10228
|
+
},
|
|
10229
|
+
{
|
|
10230
|
+
id: 'legacy',
|
|
10231
|
+
title: 'Previous Generation',
|
|
10232
|
+
icon: LI.timer,
|
|
10233
|
+
models: models.filter(m => m.legacy === true)
|
|
10234
|
+
}
|
|
10235
|
+
];
|
|
10236
|
+
return groups.filter(g => g.models.length > 0);
|
|
10237
|
+
}
|
|
10238
|
+
|
|
10239
|
+
function renderModelsGroups() {
|
|
10240
|
+
const container = document.getElementById('modelsGroups');
|
|
10241
|
+
const catalog = modelsTabData.catalog || [];
|
|
10242
|
+
const groups = getModelGroups(catalog);
|
|
10243
|
+
|
|
10244
|
+
container.innerHTML = groups.map(function(group, gi) {
|
|
10245
|
+
return '<div class="models-group" id="models-group-' + group.id + '">' +
|
|
10246
|
+
'<div class="models-group-header">' +
|
|
10247
|
+
'<span class="models-group-icon">' + lucideIcon(group.icon, 18) + '</span>' +
|
|
10248
|
+
'<span class="models-group-title">' + escapeHtml(group.title) + '</span>' +
|
|
10249
|
+
'<span class="models-group-count">' + group.models.length + '</span>' +
|
|
10250
|
+
'</div>' +
|
|
10251
|
+
'<div class="models-group-grid">' +
|
|
10252
|
+
group.models.map(function(m, mi) { return renderModelCard(m, gi * 100 + mi); }).join('') +
|
|
10253
|
+
'</div>' +
|
|
10254
|
+
'</div>';
|
|
10255
|
+
}).join('');
|
|
10256
|
+
}
|
|
10257
|
+
|
|
10258
|
+
function renderModelCard(model, seedOffset) {
|
|
10259
|
+
var typeClass = model.multimodal ? 'multimodal' :
|
|
10260
|
+
model.type === 'reranking' ? 'reranking' : 'embedding';
|
|
10261
|
+
var typeLabel = model.multimodal ? 'Multimodal' :
|
|
10262
|
+
model.type === 'reranking' ? 'Reranker' : 'Embedding';
|
|
10263
|
+
|
|
10264
|
+
var specs = [];
|
|
10265
|
+
if (model.context) specs.push({label: 'Context', value: model.context});
|
|
10266
|
+
if (model.dimensions && model.dimensions !== '\u2014') {
|
|
10267
|
+
var dimDefault = model.dimensions.split('(')[0].trim().split(',')[0];
|
|
10268
|
+
specs.push({label: 'Dims', value: dimDefault});
|
|
10269
|
+
}
|
|
10270
|
+
specs.push({label: 'Price', value: model.price});
|
|
10271
|
+
if (model.architecture) specs.push({label: 'Arch', value: model.architecture === 'moe' ? 'MoE' : 'Dense'});
|
|
10272
|
+
|
|
10273
|
+
var sharedBadge = model.sharedSpace ?
|
|
10274
|
+
'<div class="model-card-shared-badge">' + lucideIcon(LI.link, 12) + ' Shared: ' + model.sharedSpace + '</div>' : '';
|
|
10275
|
+
|
|
10276
|
+
var rtebBadge = model.rtebScore ?
|
|
10277
|
+
'<div class="model-card-rteb">' + model.rtebScore.toFixed(1) + '</div>' : '';
|
|
10278
|
+
|
|
10279
|
+
var tryActions = '';
|
|
10280
|
+
if (model.multimodal) {
|
|
10281
|
+
tryActions = '<button class="model-card-action" onclick="event.stopPropagation(); switchTab(\'multimodal\')">Try in Multimodal</button>';
|
|
10282
|
+
} else if (model.type === 'reranking') {
|
|
10283
|
+
tryActions = '<button class="model-card-action" onclick="event.stopPropagation(); switchTab(\'search\')">Try in Search</button>';
|
|
10284
|
+
} else if (!model.legacy) {
|
|
10285
|
+
tryActions = '<button class="model-card-action" onclick="event.stopPropagation(); switchTab(\'embed\')">Try in Embed</button>' +
|
|
10286
|
+
'<button class="model-card-action" onclick="event.stopPropagation(); switchTab(\'search\')">Try in Search</button>';
|
|
10287
|
+
}
|
|
10288
|
+
|
|
10289
|
+
return '<div class="model-card ' + (model.legacy ? 'legacy' : '') + '" data-model="' + model.name + '" data-type="' + typeClass + '" id="model-card-' + model.name + '">' +
|
|
10290
|
+
'<div class="model-card-nebula">' + generateNebulaSVG(140, 140, 500 + seedOffset, {variant: 'subtle', opacity: 0.05, sparkles: false}) + '</div>' +
|
|
10291
|
+
rtebBadge +
|
|
10292
|
+
'<div class="model-card-header">' +
|
|
10293
|
+
'<span class="model-card-name">' + escapeHtml(model.name) + '</span>' +
|
|
10294
|
+
'<span class="model-card-type-badge ' + typeClass + '">' + typeLabel + '</span>' +
|
|
10295
|
+
'</div>' +
|
|
10296
|
+
sharedBadge +
|
|
10297
|
+
'<div class="model-card-desc">' + escapeHtml(model.bestFor) + '</div>' +
|
|
10298
|
+
'<div class="model-card-specs">' +
|
|
10299
|
+
specs.map(function(s) {
|
|
10300
|
+
return '<div class="model-card-spec">' +
|
|
10301
|
+
'<span class="model-card-spec-label">' + s.label + '</span>' +
|
|
10302
|
+
'<span class="model-card-spec-value">' + escapeHtml(s.value) + '</span>' +
|
|
10303
|
+
'</div>';
|
|
10304
|
+
}).join('') +
|
|
10305
|
+
'</div>' +
|
|
10306
|
+
(tryActions ? '<div class="model-card-actions">' + tryActions + '</div>' : '') +
|
|
10307
|
+
'</div>';
|
|
10308
|
+
}
|
|
10309
|
+
|
|
10310
|
+
function renderModelsRtebChart() {
|
|
10311
|
+
var chart = document.getElementById('modelsRtebChart');
|
|
10312
|
+
var benchmarks = modelsTabData.benchmarks || [];
|
|
10313
|
+
if (!benchmarks.length) {
|
|
10314
|
+
chart.innerHTML = '<p style="color:var(--text-muted);font-size:13px;">Benchmark data unavailable.</p>';
|
|
10315
|
+
return;
|
|
10316
|
+
}
|
|
10317
|
+
var maxScore = Math.max.apply(null, benchmarks.map(function(b) { return b.score; }));
|
|
10318
|
+
var minDisplay = 55;
|
|
10319
|
+
|
|
10320
|
+
chart.innerHTML = benchmarks.map(function(b) {
|
|
10321
|
+
var isVoyage = b.provider === 'Voyage AI';
|
|
10322
|
+
var barPct = ((b.score - minDisplay) / (maxScore - minDisplay)) * 100;
|
|
10323
|
+
var color = isVoyage ? 'var(--accent)' : '#555';
|
|
10324
|
+
var labelColor = isVoyage ? 'var(--accent)' : 'var(--text-dim)';
|
|
10325
|
+
var fontWeight = isVoyage ? '600' : '400';
|
|
10326
|
+
|
|
10327
|
+
return '<div class="models-rteb-bar">' +
|
|
10328
|
+
'<span class="models-rteb-label" style="color:' + labelColor + ';font-weight:' + fontWeight + ';">' + escapeHtml(b.model) + '</span>' +
|
|
10329
|
+
'<div class="models-rteb-track">' +
|
|
10330
|
+
'<div class="models-rteb-fill" style="width:' + barPct.toFixed(1) + '%;background:' + color + ';' + (isVoyage ? '' : 'opacity:0.6;') + '"></div>' +
|
|
10331
|
+
'</div>' +
|
|
10332
|
+
'<span class="models-rteb-score" style="color:' + labelColor + ';">' + b.score.toFixed(2) + '</span>' +
|
|
10333
|
+
'</div>';
|
|
10334
|
+
}).join('');
|
|
10335
|
+
}
|
|
10336
|
+
|
|
10337
|
+
function scrollToModelCard(name) {
|
|
10338
|
+
var card = document.getElementById('model-card-' + name);
|
|
10339
|
+
if (card) {
|
|
10340
|
+
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
10341
|
+
card.style.outline = '2px solid var(--accent)';
|
|
10342
|
+
setTimeout(function() { card.style.outline = ''; }, 2000);
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
|
|
10346
|
+
window.filterModels = function() {
|
|
10347
|
+
var q = document.getElementById('modelsSearch').value.toLowerCase().trim();
|
|
10348
|
+
document.querySelectorAll('.model-card').forEach(function(card) {
|
|
10349
|
+
if (!q) { card.style.display = ''; return; }
|
|
10350
|
+
var text = card.textContent.toLowerCase();
|
|
10351
|
+
card.style.display = text.includes(q) ? '' : 'none';
|
|
10352
|
+
});
|
|
10353
|
+
document.querySelectorAll('.models-group').forEach(function(group) {
|
|
10354
|
+
var visible = group.querySelectorAll('.model-card:not([style*="display: none"])');
|
|
10355
|
+
group.style.display = visible.length ? '' : 'none';
|
|
10356
|
+
});
|
|
10357
|
+
};
|
|
10358
|
+
|
|
10359
|
+
window.setModelFilter = function(filter) {
|
|
10360
|
+
modelsTabData.currentFilter = filter;
|
|
10361
|
+
document.querySelectorAll('.models-filter-pill').forEach(function(p) {
|
|
10362
|
+
p.classList.toggle('active', p.dataset.filter === filter);
|
|
10363
|
+
});
|
|
10364
|
+
document.querySelectorAll('.model-card').forEach(function(card) {
|
|
10365
|
+
if (filter === 'all') { card.style.display = ''; return; }
|
|
10366
|
+
var cardType = card.dataset.type;
|
|
10367
|
+
card.style.display = cardType === filter ? '' : 'none';
|
|
10368
|
+
});
|
|
10369
|
+
document.querySelectorAll('.models-group').forEach(function(group) {
|
|
10370
|
+
var visible = group.querySelectorAll('.model-card:not([style*="display: none"])');
|
|
10371
|
+
group.style.display = visible.length ? '' : 'none';
|
|
10372
|
+
});
|
|
10373
|
+
};
|
|
10374
|
+
|
|
10375
|
+
// ── Model Picker ──
|
|
10376
|
+
var modelPickerState = {
|
|
10377
|
+
isOpen: false,
|
|
10378
|
+
targetSelectId: null,
|
|
10379
|
+
context: null,
|
|
10380
|
+
selectedModel: null,
|
|
10381
|
+
previousFocus: null,
|
|
10382
|
+
modelType: 'embedding'
|
|
10383
|
+
};
|
|
10384
|
+
|
|
10385
|
+
function openModelPicker(selectId, context) {
|
|
10386
|
+
modelPickerState.isOpen = true;
|
|
10387
|
+
modelPickerState.targetSelectId = selectId;
|
|
10388
|
+
modelPickerState.context = context;
|
|
10389
|
+
modelPickerState.previousFocus = document.activeElement;
|
|
10390
|
+
|
|
10391
|
+
var sel = document.getElementById(selectId);
|
|
10392
|
+
modelPickerState.selectedModel = sel ? sel.value : null;
|
|
10393
|
+
modelPickerState.modelType = selectId === 'searchRerankModel' ? 'reranking' : 'embedding';
|
|
10394
|
+
|
|
10395
|
+
var contextHints = {
|
|
10396
|
+
embed: 'Choose an embedding model for vectorizing text.',
|
|
10397
|
+
compare: 'Choose a model for similarity scoring.',
|
|
10398
|
+
search: 'Choose an embedding model for document search.',
|
|
10399
|
+
multimodal: 'Only multimodal models are available here.'
|
|
10400
|
+
};
|
|
10401
|
+
document.getElementById('modelPickerContext').textContent = contextHints[context] || '';
|
|
10402
|
+
|
|
10403
|
+
var rec = document.getElementById('modelPickerRecommendation');
|
|
10404
|
+
var recommendations = {
|
|
10405
|
+
embed: 'Tip: voyage-4 offers balanced quality and cost.',
|
|
10406
|
+
compare: 'Tip: voyage-4-large gives highest accuracy comparisons.',
|
|
10407
|
+
search: 'Tip: Use voyage-4-large for docs, voyage-4-lite for queries (shared space saves cost).',
|
|
10408
|
+
multimodal: 'Tip: voyage-multimodal-3.5 supports text, images, and video.'
|
|
10409
|
+
};
|
|
10410
|
+
if (recommendations[context]) {
|
|
10411
|
+
rec.textContent = recommendations[context];
|
|
10412
|
+
rec.classList.add('visible');
|
|
10413
|
+
} else {
|
|
10414
|
+
rec.classList.remove('visible');
|
|
10415
|
+
}
|
|
10416
|
+
|
|
10417
|
+
renderModelPickerList();
|
|
10418
|
+
document.getElementById('modelPickerOverlay').classList.add('open');
|
|
10419
|
+
setTimeout(function() {
|
|
10420
|
+
document.getElementById('modelPickerSearch').focus();
|
|
10421
|
+
}, 100);
|
|
10422
|
+
}
|
|
10423
|
+
|
|
10424
|
+
function closeModelPicker() {
|
|
10425
|
+
modelPickerState.isOpen = false;
|
|
10426
|
+
document.getElementById('modelPickerOverlay').classList.remove('open');
|
|
10427
|
+
document.getElementById('modelPickerSearch').value = '';
|
|
10428
|
+
if (modelPickerState.previousFocus) {
|
|
10429
|
+
modelPickerState.previousFocus.focus();
|
|
10430
|
+
modelPickerState.previousFocus = null;
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
|
|
10434
|
+
function selectModelFromPicker(name) {
|
|
10435
|
+
var sel = document.getElementById(modelPickerState.targetSelectId);
|
|
10436
|
+
if (sel) {
|
|
10437
|
+
sel.value = name;
|
|
10438
|
+
sel.dispatchEvent(new Event('change'));
|
|
10439
|
+
localStorage.setItem('vai-playground-model', name);
|
|
10440
|
+
if (modelPickerState.modelType === 'embedding') {
|
|
10441
|
+
['embedModel', 'compareModel', 'searchEmbedModel'].forEach(function(id) {
|
|
10442
|
+
var otherSel = document.getElementById(id);
|
|
10443
|
+
if (otherSel && otherSel !== sel) otherSel.value = name;
|
|
10444
|
+
});
|
|
10445
|
+
}
|
|
10446
|
+
}
|
|
10447
|
+
closeModelPicker();
|
|
10448
|
+
sendTelemetry('model_picker_select', { model: name, context: modelPickerState.context });
|
|
10449
|
+
}
|
|
10450
|
+
|
|
10451
|
+
function renderModelPickerList() {
|
|
10452
|
+
var list = document.getElementById('modelPickerList');
|
|
10453
|
+
var type = modelPickerState.modelType;
|
|
10454
|
+
var context = modelPickerState.context;
|
|
10455
|
+
var selected = modelPickerState.selectedModel;
|
|
10456
|
+
|
|
10457
|
+
var models;
|
|
10458
|
+
if (context === 'multimodal') {
|
|
10459
|
+
models = allModels.filter(function(m) { return m.multimodal; });
|
|
10460
|
+
} else if (type === 'reranking') {
|
|
10461
|
+
models = rerankModels;
|
|
10462
|
+
} else {
|
|
10463
|
+
models = embedModels;
|
|
10464
|
+
}
|
|
10465
|
+
|
|
10466
|
+
list.innerHTML = models.map(function(m) {
|
|
10467
|
+
var isSelected = m.name === selected;
|
|
10468
|
+
var tags = [];
|
|
10469
|
+
if (m.architecture === 'moe') tags.push({text: 'MoE', cls: 'model-picker-item-tag'});
|
|
10470
|
+
if (m.sharedSpace) tags.push({text: 'Shared Space', cls: 'model-picker-item-shared'});
|
|
10471
|
+
if (m.rtebScore) tags.push({text: 'RTEB ' + m.rtebScore, cls: 'model-picker-item-tag'});
|
|
10472
|
+
|
|
10473
|
+
var meta = [m.bestFor, m.context, m.price].filter(Boolean).join(' \u00b7 ');
|
|
10474
|
+
|
|
10475
|
+
return '<div class="model-picker-item ' + (isSelected ? 'selected' : '') + '" ' +
|
|
10476
|
+
'role="option" aria-selected="' + isSelected + '" tabindex="0" ' +
|
|
10477
|
+
'data-model="' + m.name + '" ' +
|
|
10478
|
+
'onclick="selectModelFromPicker(\'' + m.name + '\')" ' +
|
|
10479
|
+
'onkeydown="if(event.key===\'Enter\') selectModelFromPicker(\'' + m.name + '\')">' +
|
|
10480
|
+
'<div class="model-picker-item-radio"></div>' +
|
|
10481
|
+
'<div class="model-picker-item-info">' +
|
|
10482
|
+
'<div class="model-picker-item-name">' + escapeHtml(m.name) + '</div>' +
|
|
10483
|
+
'<div class="model-picker-item-meta">' + escapeHtml(meta) + '</div>' +
|
|
10484
|
+
(tags.length ? '<div class="model-picker-item-tags">' +
|
|
10485
|
+
tags.map(function(t) { return '<span class="' + t.cls + '">' + t.text + '</span>'; }).join('') +
|
|
10486
|
+
'</div>' : '') +
|
|
10487
|
+
'</div>' +
|
|
10488
|
+
'</div>';
|
|
10489
|
+
}).join('');
|
|
10490
|
+
}
|
|
10491
|
+
|
|
10492
|
+
window.filterModelPicker = function() {
|
|
10493
|
+
var q = document.getElementById('modelPickerSearch').value.toLowerCase().trim();
|
|
10494
|
+
document.querySelectorAll('#modelPickerList .model-picker-item').forEach(function(item) {
|
|
10495
|
+
if (!q) { item.style.display = ''; return; }
|
|
10496
|
+
var text = item.textContent.toLowerCase();
|
|
10497
|
+
item.style.display = text.includes(q) ? '' : 'none';
|
|
10498
|
+
});
|
|
10499
|
+
};
|
|
10500
|
+
|
|
10501
|
+
function enhanceModelSelects() {
|
|
10502
|
+
var selectConfigs = [
|
|
10503
|
+
{ id: 'embedModel', context: 'embed' },
|
|
10504
|
+
{ id: 'compareModel', context: 'compare' },
|
|
10505
|
+
{ id: 'searchEmbedModel', context: 'search' },
|
|
10506
|
+
{ id: 'searchRerankModel', context: 'search' }
|
|
10507
|
+
];
|
|
10508
|
+
|
|
10509
|
+
selectConfigs.forEach(function(cfg) {
|
|
10510
|
+
var sel = document.getElementById(cfg.id);
|
|
10511
|
+
if (!sel || sel.dataset.pickerEnhanced) return;
|
|
10512
|
+
sel.dataset.pickerEnhanced = 'true';
|
|
10513
|
+
|
|
10514
|
+
var pickerBtn = document.createElement('button');
|
|
10515
|
+
pickerBtn.className = 'model-picker-trigger';
|
|
10516
|
+
pickerBtn.type = 'button';
|
|
10517
|
+
pickerBtn.title = 'Browse models';
|
|
10518
|
+
pickerBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>';
|
|
10519
|
+
pickerBtn.addEventListener('click', function(e) {
|
|
10520
|
+
e.preventDefault();
|
|
10521
|
+
e.stopPropagation();
|
|
10522
|
+
openModelPicker(cfg.id, cfg.context);
|
|
10523
|
+
});
|
|
10524
|
+
|
|
10525
|
+
sel.parentNode.insertBefore(pickerBtn, sel.nextSibling);
|
|
10526
|
+
});
|
|
10527
|
+
}
|
|
10528
|
+
|
|
10529
|
+
// Model picker close handlers
|
|
10530
|
+
document.getElementById('modelPickerClose').addEventListener('click', closeModelPicker);
|
|
10531
|
+
document.getElementById('modelPickerOverlay').addEventListener('click', function(e) {
|
|
10532
|
+
if (e.target === document.getElementById('modelPickerOverlay')) closeModelPicker();
|
|
10533
|
+
});
|
|
10534
|
+
document.addEventListener('keydown', function(e) {
|
|
10535
|
+
if (e.key === 'Escape' && modelPickerState.isOpen) {
|
|
10536
|
+
e.preventDefault();
|
|
10537
|
+
closeModelPicker();
|
|
10538
|
+
}
|
|
10539
|
+
});
|
|
10540
|
+
|
|
9544
10541
|
// ── Benchmark: Sub-panel switching ──
|
|
9545
10542
|
document.querySelectorAll('.bench-panel-btn').forEach(btn => {
|
|
9546
10543
|
btn.addEventListener('click', () => {
|
|
@@ -13345,6 +14342,12 @@ const WF_NODE_META = {
|
|
|
13345
14342
|
chunk: { icon: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M9 15h6M9 11h6M9 19h4', label: 'Chunk', color: '#00D4AA', category: 'processing' },
|
|
13346
14343
|
aggregate: { icon: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z', label: 'Aggregate', color: '#00D4AA', category: 'processing' },
|
|
13347
14344
|
http: { icon: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z', label: 'HTTP Request', color: '#F5A623', category: 'integration' },
|
|
14345
|
+
// Code search tools (voyage-code-3)
|
|
14346
|
+
code_index: { icon: 'M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2zM12 11v6M9 14h6', label: 'Code Index', color: '#06B6D4', category: 'code' },
|
|
14347
|
+
code_search: { icon: 'M10 20l4-16M18 8l4 4-4 4M6 16l-4-4 4-4', label: 'Code Search', color: '#06B6D4', category: 'code' },
|
|
14348
|
+
code_query: { icon: 'M10 20l4-16M21 21l-4.3-4.3M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z', label: 'Code Query', color: '#06B6D4', category: 'code' },
|
|
14349
|
+
code_find_similar: { icon: 'M16 3h5v5M8 3H3v5M3 16v5h5M21 16v5h-5M12 8v8M8 12h8', label: 'Find Similar', color: '#06B6D4', category: 'code' },
|
|
14350
|
+
code_status: { icon: 'M22 12h-4l-3 9L9 3l-3 9H2', label: 'Code Status', color: '#06B6D4', category: 'code' },
|
|
13348
14351
|
};
|
|
13349
14352
|
|
|
13350
14353
|
// Fallback icon (gear) for unknown workflow node types
|
|
@@ -13378,6 +14381,7 @@ let wfState = {
|
|
|
13378
14381
|
draggingEdge: null,
|
|
13379
14382
|
dragNode: null,
|
|
13380
14383
|
dirtyFlag: false,
|
|
14384
|
+
outputFormat: 'auto',
|
|
13381
14385
|
};
|
|
13382
14386
|
|
|
13383
14387
|
// ── Library ──
|
|
@@ -13559,6 +14563,16 @@ function wfCloseInstallDialog() {
|
|
|
13559
14563
|
if (modal) modal.style.display = 'none';
|
|
13560
14564
|
}
|
|
13561
14565
|
|
|
14566
|
+
function wfCompareVersions(a, b) {
|
|
14567
|
+
const pa = (a || '0.0.0').split('.').map(Number);
|
|
14568
|
+
const pb = (b || '0.0.0').split('.').map(Number);
|
|
14569
|
+
for (let i = 0; i < 3; i++) {
|
|
14570
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
14571
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
14572
|
+
}
|
|
14573
|
+
return 0;
|
|
14574
|
+
}
|
|
14575
|
+
|
|
13562
14576
|
async function wfSearchNpm() {
|
|
13563
14577
|
const query = document.getElementById('wfInstallSearch').value.trim();
|
|
13564
14578
|
const results = document.getElementById('wfInstallResults');
|
|
@@ -13574,15 +14588,27 @@ async function wfSearchNpm() {
|
|
|
13574
14588
|
results.innerHTML = data.results.map(r => {
|
|
13575
14589
|
const isOfficial = r.name.startsWith('@vaicli/');
|
|
13576
14590
|
const badge = isOfficial ? '<span style="background:var(--accent);color:#fff;font-size:9px;padding:1px 5px;border-radius:3px;margin-left:6px;">OFFICIAL</span>' : '';
|
|
13577
|
-
const
|
|
13578
|
-
const
|
|
13579
|
-
|
|
13580
|
-
|
|
14591
|
+
const installedPkg = [...(wfState.official||[]), ...(wfState.community||[])].find(w => w.name === r.name);
|
|
14592
|
+
const installed = !!installedPkg;
|
|
14593
|
+
const installedVersion = installedPkg?.version;
|
|
14594
|
+
const npmVersion = r.version;
|
|
14595
|
+
const hasUpdate = installed && npmVersion && installedVersion && npmVersion !== installedVersion && wfCompareVersions(npmVersion, installedVersion) > 0;
|
|
14596
|
+
let btn;
|
|
14597
|
+
if (hasUpdate) {
|
|
14598
|
+
btn = `<button onclick="wfInstallPkg('${r.name.replace(/'/g,"\\'")}')" style="padding:4px 10px;background:#F59E0B;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px;" title="Installed: v${installedVersion}">Update</button>`;
|
|
14599
|
+
} else if (installed) {
|
|
14600
|
+
btn = '<button disabled style="padding:4px 10px;background:var(--bg);border:1px solid var(--border);border-radius:4px;color:var(--text-muted);font-size:11px;cursor:default;">Installed</button>';
|
|
14601
|
+
} else {
|
|
14602
|
+
btn = `<button onclick="wfInstallPkg('${r.name.replace(/'/g,"\\'")}')" style="padding:4px 10px;background:var(--accent);color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px;">Install</button>`;
|
|
14603
|
+
}
|
|
14604
|
+
const versionInfo = hasUpdate
|
|
14605
|
+
? `v${installedVersion} → <span style="color:#F59E0B;font-weight:500;">v${npmVersion}</span>`
|
|
14606
|
+
: `v${npmVersion || '?'}`;
|
|
13581
14607
|
return `<div style="padding:10px 12px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;justify-content:space-between;">
|
|
13582
14608
|
<div style="flex:1;min-width:0;">
|
|
13583
14609
|
<div style="font-size:13px;font-weight:500;">${r.name}${badge}</div>
|
|
13584
14610
|
<div style="font-size:11px;color:var(--text-muted);margin-top:2px;">${r.description || ''}</div>
|
|
13585
|
-
<div style="font-size:10px;color:var(--text-muted);margin-top:2px;"
|
|
14611
|
+
<div style="font-size:10px;color:var(--text-muted);margin-top:2px;">${versionInfo}</div>
|
|
13586
14612
|
</div>
|
|
13587
14613
|
<div style="margin-left:12px;flex-shrink:0;">${btn}</div>
|
|
13588
14614
|
</div>`;
|
|
@@ -13598,7 +14624,7 @@ async function wfInstallPkg(name) {
|
|
|
13598
14624
|
const btns = results.querySelectorAll('button');
|
|
13599
14625
|
let clickedBtn = null;
|
|
13600
14626
|
btns.forEach(b => {
|
|
13601
|
-
if (b.textContent === 'Install' && !clickedBtn) {
|
|
14627
|
+
if ((b.textContent === 'Install' || b.textContent === 'Update') && !clickedBtn) {
|
|
13602
14628
|
// Find the button in the same row as the package name
|
|
13603
14629
|
const row = b.closest('div[style*="border-bottom"]');
|
|
13604
14630
|
if (row && row.textContent.includes(name)) {
|
|
@@ -14388,18 +15414,27 @@ function wfUpdateInspector() {
|
|
|
14388
15414
|
|
|
14389
15415
|
// ── OUTPUT Accordion ──
|
|
14390
15416
|
const outputExpanded = hasDone || accStates.output === true;
|
|
15417
|
+
const fmtSelectorHtml = hasDone ? `<select class="wf-output-format-select" onchange="wfChangeOutputFormat(this.value)" onclick="event.stopPropagation()">
|
|
15418
|
+
<option value="auto"${wfState.outputFormat === 'auto' ? ' selected' : ''}>Auto</option>
|
|
15419
|
+
<option value="json"${wfState.outputFormat === 'json' ? ' selected' : ''}>JSON</option>
|
|
15420
|
+
<option value="table"${wfState.outputFormat === 'table' ? ' selected' : ''}>Table</option>
|
|
15421
|
+
<option value="markdown"${wfState.outputFormat === 'markdown' ? ' selected' : ''}>Markdown</option>
|
|
15422
|
+
<option value="text"${wfState.outputFormat === 'text' ? ' selected' : ''}>Text</option>
|
|
15423
|
+
</select>` : '';
|
|
14391
15424
|
html += `<div class="wf-accordion-header ${outputExpanded ? 'expanded' : ''}" onclick="wfToggleAccordion('output', this)">
|
|
14392
15425
|
<span class="wf-acc-left"><span class="wf-chevron">▶</span> OUTPUT</span>
|
|
14393
|
-
<span class="wf-acc-summary"
|
|
15426
|
+
<span class="wf-acc-summary">${fmtSelectorHtml}</span>
|
|
14394
15427
|
</div>
|
|
14395
15428
|
<div class="wf-accordion-body" data-acc="output" style="max-height:${outputExpanded ? 'none' : '0px'};">
|
|
14396
|
-
<div class="wf-accordion-body-inner">`;
|
|
15429
|
+
<div class="wf-accordion-body-inner" id="wfOutputContent">`;
|
|
14397
15430
|
if (hasDone) {
|
|
14398
15431
|
const r = wfState.executionResults._done;
|
|
14399
|
-
const
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
15432
|
+
const hints = r.formatters || {};
|
|
15433
|
+
const fmt = wfState.outputFormat || 'auto';
|
|
15434
|
+
const effectiveFmt = fmt === 'auto' ? wfAutoFormat(r.output) : fmt;
|
|
15435
|
+
html += `<div style="font-size:11px;color:var(--text-muted);margin-bottom:6px;">Completed in ${r.totalTimeMs}ms</div>`;
|
|
15436
|
+
html += wfRenderOutputAs(r.output, effectiveFmt, hints);
|
|
15437
|
+
html += `<button class="wf-output-expand-btn" data-expand-step="_done">⤢ Expand</button>`;
|
|
14403
15438
|
} else if (def.output) {
|
|
14404
15439
|
html += `<div class="wf-inspector-code" style="color:#40E0FF;">${escapeHtml(JSON.stringify(def.output, null, 2))}</div>`;
|
|
14405
15440
|
} else {
|
|
@@ -14531,11 +15566,23 @@ function wfUpdateInspector() {
|
|
|
14531
15566
|
if (state === 'completed' && result) {
|
|
14532
15567
|
const outputJson = result.output ? JSON.stringify(result.output, null, 2) : '';
|
|
14533
15568
|
const stepTitle = step.name || step.id;
|
|
15569
|
+
const stepShape = result.output ? wfDetectOutputShape(result.output) : null;
|
|
15570
|
+
const showStepFmtToggle = stepShape && (stepShape.type === 'array' || stepShape.type === 'comparison');
|
|
15571
|
+
const stepFmtToggle = showStepFmtToggle ?
|
|
15572
|
+
`<div style="margin-bottom:4px;"><select class="wf-output-format-select" style="width:auto;margin:0;" onchange="wfChangeStepFormat('${escapeHtml(step.id)}', this.value)">
|
|
15573
|
+
<option value="json">JSON</option>
|
|
15574
|
+
<option value="table" selected>Table</option>
|
|
15575
|
+
<option value="text">Text</option>
|
|
15576
|
+
</select></div>` : '';
|
|
15577
|
+
const stepResultContent = showStepFmtToggle
|
|
15578
|
+
? wfRenderOutputAs(result.output, 'table', {})
|
|
15579
|
+
: (outputJson ? '<div class="wf-inspector-code" style="max-height:120px;overflow:auto;">' + escapeHtml(outputJson) + '</div>' : '');
|
|
14534
15580
|
html += `<div class="wf-inspector-section">
|
|
14535
15581
|
<div class="wf-inspector-section-title">Result</div>
|
|
14536
15582
|
<div class="wf-inspector-result success">
|
|
14537
15583
|
<div style="font-size:11px;color:var(--text-muted);margin-bottom:4px;">${result.timeMs}ms${result.summary ? ', ' + result.summary : ''}</div>
|
|
14538
|
-
${
|
|
15584
|
+
${stepFmtToggle}
|
|
15585
|
+
<div id="wf-step-result-${escapeHtml(step.id)}">${stepResultContent}</div>
|
|
14539
15586
|
</div>
|
|
14540
15587
|
${outputJson ? '<button class="wf-output-expand-btn" data-expand-step="' + escapeHtml(step.id) + '">⤢ Expand</button>' : ''}
|
|
14541
15588
|
</div>`;
|
|
@@ -14571,17 +15618,274 @@ function wfBindExpandButtons(container) {
|
|
|
14571
15618
|
if (stepId === '_done') {
|
|
14572
15619
|
title = 'Workflow Output';
|
|
14573
15620
|
content = JSON.stringify(result.output, null, 2);
|
|
15621
|
+
// Rich modal rendering for workflow output
|
|
15622
|
+
const hints = result.formatters || {};
|
|
15623
|
+
const fmt = wfState.outputFormat || hints.default || 'auto';
|
|
15624
|
+
const effectiveFmt = fmt === 'auto' ? wfAutoFormat(result.output) : fmt;
|
|
15625
|
+
wfOpenOutputModal(title, content, effectiveFmt, result.output, hints);
|
|
15626
|
+
return;
|
|
14574
15627
|
} else {
|
|
14575
15628
|
const def = wfState.activeWorkflow;
|
|
14576
15629
|
const step = def ? def.steps.find(s => s.id === stepId) : null;
|
|
14577
15630
|
title = (step ? step.name || step.id : stepId) + ' Output';
|
|
14578
15631
|
content = JSON.stringify(result.output, null, 2);
|
|
15632
|
+
// Rich modal for step outputs with array/comparison shapes
|
|
15633
|
+
const stepShape = result.output ? wfDetectOutputShape(result.output) : null;
|
|
15634
|
+
if (stepShape && (stepShape.type === 'array' || stepShape.type === 'comparison')) {
|
|
15635
|
+
wfOpenOutputModal(title, content, 'table', result.output, {});
|
|
15636
|
+
return;
|
|
15637
|
+
}
|
|
14579
15638
|
}
|
|
14580
15639
|
wfOpenOutputModal(title, content);
|
|
14581
15640
|
});
|
|
14582
15641
|
});
|
|
14583
15642
|
}
|
|
14584
15643
|
|
|
15644
|
+
// ── Output Format Rendering ──
|
|
15645
|
+
|
|
15646
|
+
function wfStringify(val) {
|
|
15647
|
+
if (val == null) return '';
|
|
15648
|
+
if (typeof val === 'number') return val % 1 === 0 ? String(val) : val.toFixed(4);
|
|
15649
|
+
if (typeof val === 'boolean') return val ? 'true' : 'false';
|
|
15650
|
+
if (typeof val === 'object') {
|
|
15651
|
+
const s = JSON.stringify(val);
|
|
15652
|
+
return s.length > 80 ? s.slice(0, 77) + '...' : s;
|
|
15653
|
+
}
|
|
15654
|
+
return String(val);
|
|
15655
|
+
}
|
|
15656
|
+
|
|
15657
|
+
function wfDetectOutputShape(output) {
|
|
15658
|
+
if (output == null || typeof output !== 'object') return { type: 'scalar', value: output };
|
|
15659
|
+
const keys = Object.keys(output);
|
|
15660
|
+
for (const key of keys) {
|
|
15661
|
+
const val = output[key];
|
|
15662
|
+
if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'object' && val[0] !== null) {
|
|
15663
|
+
return { type: 'array', arrayKey: key, columns: Object.keys(val[0]), totalRows: val.length };
|
|
15664
|
+
}
|
|
15665
|
+
}
|
|
15666
|
+
const objKeys = keys.filter(k => output[k] != null && typeof output[k] === 'object' && !Array.isArray(output[k]));
|
|
15667
|
+
if (objKeys.length >= 2) {
|
|
15668
|
+
return { type: 'comparison', objectKeys: objKeys, metricKeys: keys.filter(k => !objKeys.includes(k)) };
|
|
15669
|
+
}
|
|
15670
|
+
const textKeys = keys.filter(k => typeof output[k] === 'string' && output[k].length > 100);
|
|
15671
|
+
if (textKeys.length > 0) {
|
|
15672
|
+
return { type: 'text', textKeys, metricKeys: keys.filter(k => !textKeys.includes(k)) };
|
|
15673
|
+
}
|
|
15674
|
+
return { type: 'metrics', keys };
|
|
15675
|
+
}
|
|
15676
|
+
|
|
15677
|
+
function wfAutoFormat(output) {
|
|
15678
|
+
const shape = wfDetectOutputShape(output);
|
|
15679
|
+
if (shape.type === 'array' || shape.type === 'comparison') return 'table';
|
|
15680
|
+
return 'text';
|
|
15681
|
+
}
|
|
15682
|
+
|
|
15683
|
+
function wfRenderOutputAs(output, format, hints) {
|
|
15684
|
+
hints = hints || {};
|
|
15685
|
+
if (!output) return '<div class="wf-inspector-code" style="color:#40E0FF;">(no output)</div>';
|
|
15686
|
+
switch (format) {
|
|
15687
|
+
case 'json':
|
|
15688
|
+
return '<div class="wf-inspector-code" style="max-height:200px;overflow:auto;color:#40E0FF;">' + escapeHtml(JSON.stringify(output, null, 2)) + '</div>';
|
|
15689
|
+
case 'table':
|
|
15690
|
+
return wfRenderTable(output, hints);
|
|
15691
|
+
case 'markdown':
|
|
15692
|
+
return '<div class="wf-output-markdown">' + renderMarkdown(wfOutputToMarkdown(output, hints)) + '</div>';
|
|
15693
|
+
case 'text':
|
|
15694
|
+
return wfRenderText(output, hints);
|
|
15695
|
+
default:
|
|
15696
|
+
return '<div class="wf-inspector-code" style="max-height:200px;overflow:auto;color:#40E0FF;">' + escapeHtml(JSON.stringify(output, null, 2)) + '</div>';
|
|
15697
|
+
}
|
|
15698
|
+
}
|
|
15699
|
+
|
|
15700
|
+
function wfRenderTable(output, hints) {
|
|
15701
|
+
const shape = wfDetectOutputShape(output);
|
|
15702
|
+
let html = '';
|
|
15703
|
+
|
|
15704
|
+
if (shape.type === 'array') {
|
|
15705
|
+
const key = hints.arrayField || shape.arrayKey;
|
|
15706
|
+
const data = output[key] || [];
|
|
15707
|
+
if (data.length === 0) return '<div style="color:var(--text-muted);font-size:12px;">(empty results)</div>';
|
|
15708
|
+
const columns = hints.columns || shape.columns;
|
|
15709
|
+
|
|
15710
|
+
// Show non-array metrics above
|
|
15711
|
+
const metricKeys = Object.keys(output).filter(k => k !== key && !Array.isArray(output[k]));
|
|
15712
|
+
if (metricKeys.length > 0) {
|
|
15713
|
+
html += '<div style="margin-bottom:8px;">';
|
|
15714
|
+
metricKeys.forEach(k => {
|
|
15715
|
+
html += '<div class="wf-output-metric"><div class="wf-output-text-label">' + escapeHtml(k) + '</div><div class="wf-output-metric-val">' + escapeHtml(wfStringify(output[k])) + '</div></div>';
|
|
15716
|
+
});
|
|
15717
|
+
html += '</div>';
|
|
15718
|
+
}
|
|
15719
|
+
|
|
15720
|
+
html += '<table class="wf-output-table"><thead><tr>';
|
|
15721
|
+
columns.forEach(c => { html += '<th>' + escapeHtml(c) + '</th>'; });
|
|
15722
|
+
html += '</tr></thead><tbody>';
|
|
15723
|
+
data.forEach(row => {
|
|
15724
|
+
html += '<tr>';
|
|
15725
|
+
columns.forEach(c => { html += '<td>' + escapeHtml(wfStringify(row[c])) + '</td>'; });
|
|
15726
|
+
html += '</tr>';
|
|
15727
|
+
});
|
|
15728
|
+
html += '</tbody></table>';
|
|
15729
|
+
return html;
|
|
15730
|
+
}
|
|
15731
|
+
|
|
15732
|
+
if (shape.type === 'comparison') {
|
|
15733
|
+
const keys = shape.objectKeys;
|
|
15734
|
+
const allFields = new Set();
|
|
15735
|
+
keys.forEach(k => { if (output[k]) Object.keys(output[k]).forEach(f => allFields.add(f)); });
|
|
15736
|
+
|
|
15737
|
+
// Show non-object metrics above
|
|
15738
|
+
const metricKeys = (shape.metricKeys || []).filter(k => output[k] != null);
|
|
15739
|
+
if (metricKeys.length > 0) {
|
|
15740
|
+
html += '<div style="margin-bottom:8px;">';
|
|
15741
|
+
metricKeys.forEach(k => {
|
|
15742
|
+
html += '<div class="wf-output-metric"><div class="wf-output-text-label">' + escapeHtml(k) + '</div><div class="wf-output-metric-val">' + escapeHtml(wfStringify(output[k])) + '</div></div>';
|
|
15743
|
+
});
|
|
15744
|
+
html += '</div>';
|
|
15745
|
+
}
|
|
15746
|
+
|
|
15747
|
+
html += '<table class="wf-output-table"><thead><tr><th></th>';
|
|
15748
|
+
keys.forEach(k => { html += '<th>' + escapeHtml(String(k)) + '</th>'; });
|
|
15749
|
+
html += '</tr></thead><tbody>';
|
|
15750
|
+
allFields.forEach(f => {
|
|
15751
|
+
html += '<tr><td style="font-weight:600;">' + escapeHtml(f) + '</td>';
|
|
15752
|
+
keys.forEach(k => { html += '<td>' + escapeHtml(wfStringify(output[k] && output[k][f])) + '</td>'; });
|
|
15753
|
+
html += '</tr>';
|
|
15754
|
+
});
|
|
15755
|
+
html += '</tbody></table>';
|
|
15756
|
+
return html;
|
|
15757
|
+
}
|
|
15758
|
+
|
|
15759
|
+
// Fallback: key-value table
|
|
15760
|
+
html += '<table class="wf-output-table"><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody>';
|
|
15761
|
+
Object.entries(output).forEach(function(entry) {
|
|
15762
|
+
html += '<tr><td>' + escapeHtml(entry[0]) + '</td><td>' + escapeHtml(wfStringify(entry[1])) + '</td></tr>';
|
|
15763
|
+
});
|
|
15764
|
+
html += '</tbody></table>';
|
|
15765
|
+
return html;
|
|
15766
|
+
}
|
|
15767
|
+
|
|
15768
|
+
function wfRenderText(output, hints) {
|
|
15769
|
+
const shape = wfDetectOutputShape(output);
|
|
15770
|
+
if (shape.type === 'scalar') return '<div style="font-size:12px;color:var(--text);">' + escapeHtml(String(output || '')) + '</div>';
|
|
15771
|
+
let html = '';
|
|
15772
|
+
if (hints.title) {
|
|
15773
|
+
html += '<div style="font-weight:600;font-size:13px;color:var(--accent);margin-bottom:8px;">' + escapeHtml(hints.title) + '</div>';
|
|
15774
|
+
}
|
|
15775
|
+
|
|
15776
|
+
// Metrics
|
|
15777
|
+
const metricKeys = Object.keys(output).filter(k =>
|
|
15778
|
+
output[k] != null && (typeof output[k] !== 'object' || typeof output[k] === 'boolean') &&
|
|
15779
|
+
(typeof output[k] !== 'string' || output[k].length <= 100)
|
|
15780
|
+
);
|
|
15781
|
+
if (metricKeys.length > 0) {
|
|
15782
|
+
html += '<div style="margin-bottom:10px;">';
|
|
15783
|
+
metricKeys.forEach(k => {
|
|
15784
|
+
html += '<div class="wf-output-metric"><div class="wf-output-text-label">' + escapeHtml(k) + '</div><div class="wf-output-metric-val">' + escapeHtml(wfStringify(output[k])) + '</div></div>';
|
|
15785
|
+
});
|
|
15786
|
+
html += '</div>';
|
|
15787
|
+
}
|
|
15788
|
+
|
|
15789
|
+
// Object fields (comparison-like)
|
|
15790
|
+
Object.keys(output).forEach(k => {
|
|
15791
|
+
const v = output[k];
|
|
15792
|
+
if (v != null && typeof v === 'object' && !Array.isArray(v)) {
|
|
15793
|
+
html += '<div class="wf-output-text-field"><div class="wf-output-text-label">' + escapeHtml(k) + '</div>';
|
|
15794
|
+
Object.entries(v).forEach(function(entry) {
|
|
15795
|
+
html += '<div style="font-size:12px;margin-left:8px;"><span style="color:var(--text-muted);">' + escapeHtml(entry[0]) + ':</span> <span style="color:#40E0FF;">' + escapeHtml(wfStringify(entry[1])) + '</span></div>';
|
|
15796
|
+
});
|
|
15797
|
+
html += '</div>';
|
|
15798
|
+
}
|
|
15799
|
+
});
|
|
15800
|
+
|
|
15801
|
+
// Text fields
|
|
15802
|
+
Object.keys(output).forEach(k => {
|
|
15803
|
+
if (typeof output[k] === 'string' && output[k].length > 100) {
|
|
15804
|
+
html += '<div class="wf-output-text-field"><div class="wf-output-text-label">' + escapeHtml(k) + '</div><div class="wf-output-text-value">' + escapeHtml(output[k]) + '</div></div>';
|
|
15805
|
+
}
|
|
15806
|
+
});
|
|
15807
|
+
|
|
15808
|
+
// Arrays
|
|
15809
|
+
Object.keys(output).forEach(k => {
|
|
15810
|
+
if (Array.isArray(output[k])) {
|
|
15811
|
+
html += '<div class="wf-output-text-field"><div class="wf-output-text-label">' + escapeHtml(k) + ' (' + output[k].length + ' items)</div>';
|
|
15812
|
+
html += '<div class="wf-inspector-code" style="max-height:100px;overflow:auto;">' + escapeHtml(JSON.stringify(output[k], null, 2)) + '</div></div>';
|
|
15813
|
+
}
|
|
15814
|
+
});
|
|
15815
|
+
|
|
15816
|
+
return html || '<div class="wf-inspector-code" style="color:#40E0FF;">' + escapeHtml(JSON.stringify(output, null, 2)) + '</div>';
|
|
15817
|
+
}
|
|
15818
|
+
|
|
15819
|
+
function wfOutputToMarkdown(output, hints) {
|
|
15820
|
+
let md = '';
|
|
15821
|
+
if (hints.title) md += '## ' + hints.title + '\n\n';
|
|
15822
|
+
else md += '## Workflow Output\n\n';
|
|
15823
|
+
Object.entries(output).forEach(function(entry) {
|
|
15824
|
+
const k = entry[0], v = entry[1];
|
|
15825
|
+
if (v == null) return;
|
|
15826
|
+
if (typeof v !== 'object' || typeof v === 'boolean') {
|
|
15827
|
+
if (typeof v !== 'string' || v.length <= 100) {
|
|
15828
|
+
md += '- **' + k + ':** ' + wfStringify(v) + '\n';
|
|
15829
|
+
}
|
|
15830
|
+
}
|
|
15831
|
+
});
|
|
15832
|
+
md += '\n';
|
|
15833
|
+
// Arrays as tables
|
|
15834
|
+
Object.entries(output).forEach(function(entry) {
|
|
15835
|
+
const k = entry[0], v = entry[1];
|
|
15836
|
+
if (!Array.isArray(v) || v.length === 0 || typeof v[0] !== 'object') return;
|
|
15837
|
+
const cols = hints.columns || Object.keys(v[0]);
|
|
15838
|
+
md += '### ' + k + '\n\n| ' + cols.join(' | ') + ' |\n| ' + cols.map(function() { return '---'; }).join(' | ') + ' |\n';
|
|
15839
|
+
v.forEach(function(row) { md += '| ' + cols.map(function(c) { return wfStringify(row[c]); }).join(' | ') + ' |\n'; });
|
|
15840
|
+
md += '\n';
|
|
15841
|
+
});
|
|
15842
|
+
// Comparison objects
|
|
15843
|
+
const objKeys = Object.keys(output).filter(function(k) { return output[k] != null && typeof output[k] === 'object' && !Array.isArray(output[k]); });
|
|
15844
|
+
if (objKeys.length >= 2) {
|
|
15845
|
+
const allFields = new Set();
|
|
15846
|
+
objKeys.forEach(function(k) { Object.keys(output[k]).forEach(function(f) { allFields.add(f); }); });
|
|
15847
|
+
md += '| | ' + objKeys.join(' | ') + ' |\n| --- | ' + objKeys.map(function() { return '---'; }).join(' | ') + ' |\n';
|
|
15848
|
+
allFields.forEach(function(f) {
|
|
15849
|
+
md += '| **' + f + '** | ' + objKeys.map(function(k) { return wfStringify(output[k][f]); }).join(' | ') + ' |\n';
|
|
15850
|
+
});
|
|
15851
|
+
md += '\n';
|
|
15852
|
+
}
|
|
15853
|
+
// Long text
|
|
15854
|
+
Object.entries(output).forEach(function(entry) {
|
|
15855
|
+
if (typeof entry[1] === 'string' && entry[1].length > 100) {
|
|
15856
|
+
md += '### ' + entry[0] + '\n\n' + entry[1] + '\n\n';
|
|
15857
|
+
}
|
|
15858
|
+
});
|
|
15859
|
+
return md;
|
|
15860
|
+
}
|
|
15861
|
+
|
|
15862
|
+
function wfChangeOutputFormat(format) {
|
|
15863
|
+
wfState.outputFormat = format;
|
|
15864
|
+
const r = wfState.executionResults._done;
|
|
15865
|
+
if (!r) return;
|
|
15866
|
+
const hints = r.formatters || {};
|
|
15867
|
+
const effectiveFormat = format === 'auto' ? wfAutoFormat(r.output) : format;
|
|
15868
|
+
const container = document.getElementById('wfOutputContent');
|
|
15869
|
+
if (!container) return;
|
|
15870
|
+
let html = '<div style="font-size:11px;color:var(--text-muted);margin-bottom:6px;">Completed in ' + r.totalTimeMs + 'ms</div>';
|
|
15871
|
+
html += wfRenderOutputAs(r.output, effectiveFormat, hints);
|
|
15872
|
+
html += '<button class="wf-output-expand-btn" data-expand-step="_done">⤢ Expand</button>';
|
|
15873
|
+
container.innerHTML = html;
|
|
15874
|
+
wfBindExpandButtons(container);
|
|
15875
|
+
}
|
|
15876
|
+
|
|
15877
|
+
function wfChangeStepFormat(stepId, format) {
|
|
15878
|
+
const result = wfState.executionResults[stepId];
|
|
15879
|
+
if (!result || !result.output) return;
|
|
15880
|
+
const container = document.getElementById('wf-step-result-' + stepId);
|
|
15881
|
+
if (!container) return;
|
|
15882
|
+
if (format === 'json') {
|
|
15883
|
+
container.innerHTML = '<div class="wf-inspector-code" style="max-height:120px;overflow:auto;">' + escapeHtml(JSON.stringify(result.output, null, 2)) + '</div>';
|
|
15884
|
+
} else {
|
|
15885
|
+
container.innerHTML = wfRenderOutputAs(result.output, format, {});
|
|
15886
|
+
}
|
|
15887
|
+
}
|
|
15888
|
+
|
|
14585
15889
|
// escapeHtml is already defined globally — reuse it
|
|
14586
15890
|
|
|
14587
15891
|
// ── Toolbar helpers ──
|
|
@@ -14783,6 +16087,31 @@ function wfStopExecution(reason) {
|
|
|
14783
16087
|
wfUpdateInspector();
|
|
14784
16088
|
}
|
|
14785
16089
|
|
|
16090
|
+
// ── Workflow Input Cache (localStorage) ──
|
|
16091
|
+
|
|
16092
|
+
function wfSlugify(name) {
|
|
16093
|
+
return String(name).toLowerCase().replace(/[\s_]+/g, '-').replace(/[^a-z0-9-]/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
16094
|
+
}
|
|
16095
|
+
|
|
16096
|
+
function wfGetInputCache(workflowName) {
|
|
16097
|
+
try {
|
|
16098
|
+
const slug = wfSlugify(workflowName);
|
|
16099
|
+
if (!slug) return {};
|
|
16100
|
+
const raw = localStorage.getItem('vai-workflow-inputs-' + slug);
|
|
16101
|
+
if (!raw) return {};
|
|
16102
|
+
const parsed = JSON.parse(raw);
|
|
16103
|
+
return (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) ? parsed : {};
|
|
16104
|
+
} catch { return {}; }
|
|
16105
|
+
}
|
|
16106
|
+
|
|
16107
|
+
function wfSaveInputCache(workflowName, inputs) {
|
|
16108
|
+
try {
|
|
16109
|
+
const slug = wfSlugify(workflowName);
|
|
16110
|
+
if (!slug || !inputs) return;
|
|
16111
|
+
localStorage.setItem('vai-workflow-inputs-' + slug, JSON.stringify(inputs));
|
|
16112
|
+
} catch { /* localStorage may be full or disabled */ }
|
|
16113
|
+
}
|
|
16114
|
+
|
|
14786
16115
|
// ── Input Modal (pre-execution) ──
|
|
14787
16116
|
|
|
14788
16117
|
function wfShowInputModal() {
|
|
@@ -14792,13 +16121,15 @@ function wfShowInputModal() {
|
|
|
14792
16121
|
if (entries.length === 0) { wfExecuteWithInputs({}); return; }
|
|
14793
16122
|
|
|
14794
16123
|
document.getElementById('wfInputModalTitle').textContent = (def.name || 'Workflow') + ' Inputs';
|
|
16124
|
+
const cached = wfGetInputCache(def.name || '');
|
|
14795
16125
|
let html = '';
|
|
14796
16126
|
for (const [key, spec] of entries) {
|
|
14797
16127
|
const req = spec.required ? ' <span style="color:#e74c3c">*</span>' : '';
|
|
14798
16128
|
const desc = spec.description ? `<div class="wf-input-modal-desc">${escapeHtml(spec.description)}</div>` : '';
|
|
14799
|
-
// Pre-fill
|
|
16129
|
+
// Pre-fill priority: 1) inspector field, 2) cached from last run, 3) spec default
|
|
14800
16130
|
const inspectorEl = document.getElementById('wf-input-' + key);
|
|
14801
16131
|
let prefill = inspectorEl ? inspectorEl.value : '';
|
|
16132
|
+
if (!prefill && key in cached) prefill = String(cached[key]);
|
|
14802
16133
|
if (!prefill && spec.default !== undefined) prefill = String(spec.default);
|
|
14803
16134
|
const placeholder = spec.type === 'number' ? 'number' : (spec.type || 'string');
|
|
14804
16135
|
html += `<div class="wf-input-modal-field">
|
|
@@ -14869,6 +16200,9 @@ function wfInputModalSubmit() {
|
|
|
14869
16200
|
|
|
14870
16201
|
if (hasError) return;
|
|
14871
16202
|
|
|
16203
|
+
// Cache inputs for next run
|
|
16204
|
+
wfSaveInputCache(def.name || '', inputs);
|
|
16205
|
+
|
|
14872
16206
|
// Also update inspector fields to keep them in sync
|
|
14873
16207
|
for (const [key, val] of Object.entries(inputs)) {
|
|
14874
16208
|
const inspEl = document.getElementById('wf-input-' + key);
|
|
@@ -14988,6 +16322,7 @@ async function wfExecuteWithInputs(inputs) {
|
|
|
14988
16322
|
if (wfState.selectedNodeId === data.stepId) wfUpdateInspector();
|
|
14989
16323
|
} else if (currentEvent === 'done') {
|
|
14990
16324
|
wfState.executionResults._done = data;
|
|
16325
|
+
wfState.outputFormat = 'auto';
|
|
14991
16326
|
if (!wfState.selectedNodeId) wfUpdateInspector();
|
|
14992
16327
|
} else if (currentEvent === 'error') {
|
|
14993
16328
|
hasError = true;
|
|
@@ -15077,7 +16412,7 @@ function wfResetExecution() {
|
|
|
15077
16412
|
// ── Output Modal ──
|
|
15078
16413
|
let wfOutputModalData = '';
|
|
15079
16414
|
|
|
15080
|
-
function wfOpenOutputModal(title, content) {
|
|
16415
|
+
function wfOpenOutputModal(title, content, format, outputObj, hints) {
|
|
15081
16416
|
wfOutputModalData = content;
|
|
15082
16417
|
const backdrop = document.getElementById('wfOutputModalBackdrop');
|
|
15083
16418
|
const titleEl = document.getElementById('wfOutputModalTitle');
|
|
@@ -15085,8 +16420,14 @@ function wfOpenOutputModal(title, content) {
|
|
|
15085
16420
|
const copyLabel = document.getElementById('wfOutputCopyLabel');
|
|
15086
16421
|
if (!backdrop || !bodyEl) return;
|
|
15087
16422
|
titleEl.textContent = title || 'Output';
|
|
15088
|
-
bodyEl.textContent = content;
|
|
15089
16423
|
copyLabel.textContent = 'Copy';
|
|
16424
|
+
|
|
16425
|
+
// Rich rendering if format + output object provided
|
|
16426
|
+
if (format && outputObj && format !== 'json') {
|
|
16427
|
+
bodyEl.innerHTML = wfRenderOutputAs(outputObj, format, hints || {});
|
|
16428
|
+
} else {
|
|
16429
|
+
bodyEl.textContent = content;
|
|
16430
|
+
}
|
|
15090
16431
|
backdrop.style.display = 'flex';
|
|
15091
16432
|
}
|
|
15092
16433
|
|
|
@@ -15325,7 +16666,7 @@ const WF_INPUT_DEFS = {
|
|
|
15325
16666
|
search: [{ key: 'query', type: 'text', required: true, placeholder: 'Search query' }, { key: 'collection', type: 'text', required: false }, { key: 'db', type: 'text', required: false }, { key: 'limit', type: 'number', required: false, placeholder: '10' }, { key: 'filter', type: 'json', required: false, placeholder: '{}' }],
|
|
15326
16667
|
rerank: [{ key: 'query', type: 'text', required: true }, { key: 'documents', type: 'json', required: true, placeholder: '["doc1","doc2"]' }, { key: 'model', type: 'text', required: false, placeholder: 'rerank-2.5' }],
|
|
15327
16668
|
ingest: [{ key: 'text', type: 'textarea', required: true }, { key: 'collection', type: 'text', required: false }, { key: 'db', type: 'text', required: false }, { key: 'source', type: 'text', required: false }, { key: 'chunkSize', type: 'number', required: false, placeholder: '512' }, { key: 'chunkStrategy', type: 'select', required: false, options: ['fixed','sentence','paragraph','recursive','markdown'] }],
|
|
15328
|
-
embed: [{ key: 'text', type: 'text', required: true, placeholder: 'Text to embed' }, { key: 'model', type: 'text', required: false, placeholder: 'voyage-
|
|
16669
|
+
embed: [{ key: 'text', type: 'text', required: true, placeholder: 'Text to embed' }, { key: 'model', type: 'text', required: false, placeholder: 'voyage-4-large' }, { key: 'inputType', type: 'select', required: false, options: ['document','query'] }],
|
|
15329
16670
|
similarity: [{ key: 'text1', type: 'text', required: true }, { key: 'text2', type: 'text', required: true }, { key: 'model', type: 'text', required: false }],
|
|
15330
16671
|
collections: [{ key: 'db', type: 'text', required: false }],
|
|
15331
16672
|
models: [{ key: 'category', type: 'select', required: false, options: ['embedding','rerank','all'] }],
|
|
@@ -18184,6 +19525,7 @@ const staticCommandRegistry = [
|
|
|
18184
19525
|
{ id: 'nav-generate', label: 'Generate', description: 'Code generation and templates', category: 'navigation', keywords: ['code', 'template', 'scaffold'], icon: '💻', shortcut: '⌘6', priority: 1, execute: () => switchTab('generate') },
|
|
18185
19526
|
{ id: 'nav-chat', label: 'Chat', description: 'Chat with your knowledge base', category: 'navigation', keywords: ['conversation', 'ask', 'rag', 'talk'], icon: '💬', shortcut: '⌘7', priority: 1, execute: () => switchTab('chat') },
|
|
18186
19527
|
{ id: 'nav-workflows', label: 'Workflows', description: 'Visual workflow canvas', category: 'navigation', keywords: ['pipeline', 'dag', 'canvas'], icon: '🔄', shortcut: '⌘8', priority: 1, execute: () => switchTab('workflows') },
|
|
19528
|
+
{ id: 'nav-models', label: 'Models', description: 'Voyage AI model catalog and specs', category: 'navigation', keywords: ['model', 'voyage', 'catalog', 'specs', 'embedding', 'rerank', 'pricing'], icon: '🤖', priority: 1, execute: () => switchTab('models') },
|
|
18187
19529
|
{ id: 'nav-benchmark', label: 'Benchmark', description: 'Model comparison on your data', category: 'navigation', keywords: ['performance', 'test', 'evaluate', 'benchmark'], icon: '📊', shortcut: '⌘9', priority: 1, execute: () => switchTab('benchmark') },
|
|
18188
19530
|
{ id: 'nav-explore', label: 'Explore', description: 'Embedding space visualization', category: 'navigation', keywords: ['pca', 'tsne', 'visualize', 'scatter'], icon: '🎯', shortcut: '⌘0', priority: 1, execute: () => switchTab('explore') },
|
|
18189
19531
|
{ id: 'nav-about', label: 'About', description: 'Version and project information', category: 'navigation', keywords: ['version', 'info', 'credits'], icon: 'ℹ️', priority: 5, execute: () => switchTab('about') },
|
|
@@ -18239,7 +19581,7 @@ const staticCommandRegistry = [
|
|
|
18239
19581
|
{ id: 'explain-reranking', label: 'Learn: Reranking', description: 'Two-stage retrieval with rerankers', category: 'explainer', keywords: ['reranking', 'two-stage', 'retrieval', 'precision'], icon: '📚', priority: 7, execute: () => switchTab('explore') },
|
|
18240
19582
|
{ id: 'explain-vector-search', label: 'Learn: Vector Search', description: 'MongoDB Atlas Vector Search', category: 'explainer', keywords: ['vector', 'search', 'atlas', 'similarity', 'knn'], icon: '📚', priority: 7, execute: () => switchTab('explore') },
|
|
18241
19583
|
{ id: 'explain-rag', label: 'Learn: RAG Pipelines', description: 'Retrieval-Augmented Generation', category: 'explainer', keywords: ['rag', 'retrieval', 'augmented', 'generation', 'pipeline'], icon: '📚', priority: 7, execute: () => switchTab('explore') },
|
|
18242
|
-
{ id: 'explain-models', label: 'Learn: Voyage AI Models', description: 'Choosing the right model', category: 'explainer', keywords: ['models', 'voyage-4', 'large', 'lite', 'domain'], icon: '📚', priority: 7, execute: () => switchTab('
|
|
19584
|
+
{ id: 'explain-models', label: 'Learn: Voyage AI Models', description: 'Choosing the right model', category: 'explainer', keywords: ['models', 'voyage-4', 'large', 'lite', 'domain'], icon: '📚', priority: 7, execute: () => switchTab('models') },
|
|
18243
19585
|
{ id: 'explain-quantization', label: 'Learn: Quantization', description: 'Reduce storage costs with lower-precision embeddings', category: 'explainer', keywords: ['quantization', 'int8', 'ubinary', 'compression', 'storage'], icon: '📚', priority: 7, execute: () => switchTab('explore') }
|
|
18244
19586
|
];
|
|
18245
19587
|
|