@objectstack/service-settings 6.5.1 → 6.7.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.
package/dist/index.cjs CHANGED
@@ -199,20 +199,20 @@ var SettingsService = class {
199
199
  // Manifest registry
200
200
  // ---------------------------------------------------------------------
201
201
  /** Register (or replace) a manifest. Idempotent. */
202
- registerManifest(manifest3) {
202
+ registerManifest(manifest5) {
203
203
  const scopes = /* @__PURE__ */ new Map();
204
204
  const encryptedKeys = /* @__PURE__ */ new Set();
205
205
  const defaults = /* @__PURE__ */ new Map();
206
- const defaultScope = manifest3.scope ?? "tenant";
207
- for (const spec of manifest3.specifiers) {
206
+ const defaultScope = manifest5.scope ?? "tenant";
207
+ for (const spec of manifest5.specifiers) {
208
208
  if (!spec.key || LAYOUT_ONLY_TYPES.has(spec.type)) continue;
209
209
  scopes.set(spec.key, spec.scope ?? defaultScope);
210
210
  if (spec.encrypted || spec.type === "password") encryptedKeys.add(spec.key);
211
211
  if (typeof spec.default !== "undefined") defaults.set(spec.key, spec.default);
212
212
  }
213
- const prev = this.registry.get(manifest3.namespace);
213
+ const prev = this.registry.get(manifest5.namespace);
214
214
  const actions = prev?.actions ?? /* @__PURE__ */ new Map();
215
- this.registry.set(manifest3.namespace, { manifest: manifest3, scopes, encryptedKeys, defaults, actions });
215
+ this.registry.set(manifest5.namespace, { manifest: manifest5, scopes, encryptedKeys, defaults, actions });
216
216
  }
217
217
  /** Look up a manifest, or throw `UnknownNamespaceError`. */
218
218
  getManifest(namespace) {
@@ -1199,11 +1199,512 @@ var storageTestActionHandler = async ({ values }) => {
1199
1199
  return { ok: false, severity: "error", message: `Unknown adapter: ${adapter}` };
1200
1200
  };
1201
1201
 
1202
+ // src/manifests/ai.manifest.ts
1203
+ var manifest3 = {
1204
+ namespace: "ai",
1205
+ version: 1,
1206
+ label: "AI",
1207
+ icon: "Sparkles",
1208
+ description: "LLM provider, model, credentials, and embedder configuration used by the platform AI and knowledge services. Provider SDK packages (e.g. @ai-sdk/openai for chat, @objectstack/embedder-openai for embeddings) must be installed on the host for the chosen provider to be loadable at runtime.",
1209
+ scope: "global",
1210
+ readPermission: "setup.access",
1211
+ writePermission: "setup.write",
1212
+ category: "Infrastructure",
1213
+ order: 30,
1214
+ specifiers: [
1215
+ // ── Provider selection ────────────────────────────────────────
1216
+ {
1217
+ type: "group",
1218
+ id: "provider",
1219
+ label: "Provider",
1220
+ required: false,
1221
+ description: "Choose the LLM backend. Memory mode echoes input \u2014 useful for tests but never for production."
1222
+ },
1223
+ {
1224
+ type: "select",
1225
+ key: "provider",
1226
+ label: "Provider",
1227
+ required: true,
1228
+ default: "memory",
1229
+ options: [
1230
+ { value: "memory", label: "Memory (echo \u2014 testing only)" },
1231
+ { value: "gateway", label: "Vercel AI Gateway" },
1232
+ { value: "openai", label: "OpenAI" },
1233
+ { value: "anthropic", label: "Anthropic" },
1234
+ { value: "google", label: "Google Generative AI" }
1235
+ ]
1236
+ },
1237
+ // ── Vercel AI Gateway ─────────────────────────────────────────
1238
+ {
1239
+ type: "group",
1240
+ id: "gateway",
1241
+ label: "Vercel AI Gateway",
1242
+ required: false,
1243
+ visible: "${data.provider === 'gateway'}",
1244
+ description: "Multi-provider router. The model spec follows `provider/model`, e.g. `openai/gpt-4o`."
1245
+ },
1246
+ {
1247
+ type: "text",
1248
+ key: "gateway_model",
1249
+ label: "Gateway model",
1250
+ required: true,
1251
+ description: "Forwarded as AI_GATEWAY_MODEL. Example: openai/gpt-4o",
1252
+ visible: "${data.provider === 'gateway'}"
1253
+ },
1254
+ {
1255
+ type: "password",
1256
+ key: "gateway_api_key",
1257
+ label: "Gateway API key",
1258
+ required: false,
1259
+ encrypted: true,
1260
+ description: "Optional \u2014 required only if the gateway enforces auth.",
1261
+ visible: "${data.provider === 'gateway'}"
1262
+ },
1263
+ // ── OpenAI ───────────────────────────────────────────────────
1264
+ {
1265
+ type: "group",
1266
+ id: "openai",
1267
+ label: "OpenAI",
1268
+ required: false,
1269
+ visible: "${data.provider === 'openai'}"
1270
+ },
1271
+ {
1272
+ type: "password",
1273
+ key: "openai_api_key",
1274
+ label: "OpenAI API key",
1275
+ required: true,
1276
+ encrypted: true,
1277
+ description: "Forwarded as OPENAI_API_KEY. Stored encrypted at rest.",
1278
+ visible: "${data.provider === 'openai'}"
1279
+ },
1280
+ {
1281
+ type: "text",
1282
+ key: "openai_model",
1283
+ label: "Model",
1284
+ required: false,
1285
+ default: "gpt-4o",
1286
+ description: "Default model id. Per-agent overrides take precedence.",
1287
+ visible: "${data.provider === 'openai'}"
1288
+ },
1289
+ {
1290
+ type: "text",
1291
+ key: "openai_base_url",
1292
+ label: "Base URL",
1293
+ required: false,
1294
+ description: "Override for Azure OpenAI or self-hosted gateways. Leave blank for api.openai.com.",
1295
+ visible: "${data.provider === 'openai'}"
1296
+ },
1297
+ // ── Anthropic ────────────────────────────────────────────────
1298
+ {
1299
+ type: "group",
1300
+ id: "anthropic",
1301
+ label: "Anthropic",
1302
+ required: false,
1303
+ visible: "${data.provider === 'anthropic'}"
1304
+ },
1305
+ {
1306
+ type: "password",
1307
+ key: "anthropic_api_key",
1308
+ label: "Anthropic API key",
1309
+ required: true,
1310
+ encrypted: true,
1311
+ description: "Forwarded as ANTHROPIC_API_KEY. Stored encrypted at rest.",
1312
+ visible: "${data.provider === 'anthropic'}"
1313
+ },
1314
+ {
1315
+ type: "text",
1316
+ key: "anthropic_model",
1317
+ label: "Model",
1318
+ required: false,
1319
+ default: "claude-sonnet-4-20250514",
1320
+ visible: "${data.provider === 'anthropic'}"
1321
+ },
1322
+ // ── Google Generative AI ─────────────────────────────────────
1323
+ {
1324
+ type: "group",
1325
+ id: "google",
1326
+ label: "Google",
1327
+ required: false,
1328
+ visible: "${data.provider === 'google'}"
1329
+ },
1330
+ {
1331
+ type: "password",
1332
+ key: "google_api_key",
1333
+ label: "Google API key",
1334
+ required: true,
1335
+ encrypted: true,
1336
+ description: "Forwarded as GOOGLE_GENERATIVE_AI_API_KEY. Stored encrypted at rest.",
1337
+ visible: "${data.provider === 'google'}"
1338
+ },
1339
+ {
1340
+ type: "text",
1341
+ key: "google_model",
1342
+ label: "Model",
1343
+ required: false,
1344
+ default: "gemini-2.0-flash",
1345
+ visible: "${data.provider === 'google'}"
1346
+ },
1347
+ // ── Generation defaults ──────────────────────────────────────
1348
+ {
1349
+ type: "group",
1350
+ id: "defaults",
1351
+ label: "Generation defaults",
1352
+ required: false,
1353
+ description: "Applied when an agent or chat request does not specify its own value.",
1354
+ visible: "${data.provider !== 'memory'}"
1355
+ },
1356
+ {
1357
+ type: "slider",
1358
+ key: "temperature",
1359
+ label: "Temperature",
1360
+ required: false,
1361
+ default: 0.7,
1362
+ min: 0,
1363
+ max: 2,
1364
+ step: 0.1,
1365
+ description: "0 = deterministic, 2 = highly creative.",
1366
+ visible: "${data.provider !== 'memory'}"
1367
+ },
1368
+ {
1369
+ type: "number",
1370
+ key: "max_tokens",
1371
+ label: "Max output tokens",
1372
+ required: false,
1373
+ default: 4096,
1374
+ min: 1,
1375
+ max: 1048576,
1376
+ description: "Hard cap on tokens generated per response.",
1377
+ visible: "${data.provider !== 'memory'}"
1378
+ },
1379
+ {
1380
+ type: "number",
1381
+ key: "request_timeout_ms",
1382
+ label: "Request timeout (ms)",
1383
+ required: false,
1384
+ default: 6e4,
1385
+ min: 1e3,
1386
+ max: 6e5,
1387
+ visible: "${data.provider !== 'memory'}"
1388
+ },
1389
+ // ── Observability ────────────────────────────────────────────
1390
+ { type: "group", id: "observability", label: "Observability", required: false },
1391
+ {
1392
+ type: "toggle",
1393
+ key: "trace_enabled",
1394
+ label: "Record traces",
1395
+ required: false,
1396
+ default: true,
1397
+ description: "Persist prompt/response traces to sys_ai_trace for debugging and replay."
1398
+ },
1399
+ {
1400
+ type: "toggle",
1401
+ key: "log_prompts",
1402
+ label: "Log full prompts",
1403
+ required: false,
1404
+ default: false,
1405
+ description: "Include rendered prompts (not just metadata) in trace rows. \u26A0 May leak PII \u2014 disable in regulated environments."
1406
+ },
1407
+ // ── Probe ────────────────────────────────────────────────────
1408
+ {
1409
+ type: "action_button",
1410
+ id: "test",
1411
+ label: "Test connection",
1412
+ required: false,
1413
+ icon: "Plug",
1414
+ handler: { kind: "http", method: "POST", url: "/api/settings/ai/test" }
1415
+ },
1416
+ // ════════════════════════════════════════════════════════════════
1417
+ // Embedder — text → vector provider used by knowledge / RAG.
1418
+ // Decoupled from the chat provider above so an organisation can
1419
+ // mix-and-match (e.g. OpenAI for chat + 阿里通义 for embeddings).
1420
+ //
1421
+ // The preset list mirrors @objectstack/embedder-openai's
1422
+ // OPENAI_COMPATIBLE_PRESETS so a UI dropdown maps 1:1 to a
1423
+ // runtime baseUrl. The "none" choice is the explicit opt-out
1424
+ // for instances that disable knowledge / RAG entirely.
1425
+ // ════════════════════════════════════════════════════════════════
1426
+ {
1427
+ type: "group",
1428
+ id: "embedder",
1429
+ label: "Embedder",
1430
+ required: false,
1431
+ description: "Text \u2192 vector provider used by knowledge sources and RAG. Independent from the chat provider above \u2014 mix providers freely (e.g. OpenAI for chat + \u963F\u91CC\u901A\u4E49 for embeddings)."
1432
+ },
1433
+ {
1434
+ type: "select",
1435
+ key: "embedder_provider",
1436
+ label: "Provider",
1437
+ required: false,
1438
+ default: "none",
1439
+ options: [
1440
+ { value: "none", label: "Disabled (no embeddings)" },
1441
+ { value: "openai", label: "OpenAI" },
1442
+ { value: "azure", label: "Azure OpenAI" },
1443
+ { value: "dashscope", label: "\u963F\u91CC\u901A\u4E49 DashScope" },
1444
+ { value: "zhipu", label: "\u667A\u8C31 BigModel" },
1445
+ { value: "siliconflow", label: "\u7845\u57FA\u6D41\u52A8 SiliconFlow" },
1446
+ { value: "doubao", label: "\u706B\u5C71\u5F15\u64CE Doubao" },
1447
+ { value: "minimax", label: "MiniMax" },
1448
+ { value: "ollama", label: "Ollama (local)" },
1449
+ { value: "custom", label: "Custom (OpenAI-compatible)" }
1450
+ ]
1451
+ },
1452
+ {
1453
+ type: "password",
1454
+ key: "embedder_api_key",
1455
+ label: "Embedder API key",
1456
+ required: false,
1457
+ encrypted: true,
1458
+ description: "Bearer token sent as Authorization header. For Ollama any non-empty value works.",
1459
+ visible: "${data.embedder_provider && data.embedder_provider !== 'none'}"
1460
+ },
1461
+ {
1462
+ type: "text",
1463
+ key: "embedder_model",
1464
+ label: "Model",
1465
+ required: false,
1466
+ description: "Examples \u2014 OpenAI: text-embedding-3-small \xB7 \u963F\u91CC\u901A\u4E49: text-embedding-v3 \xB7 \u667A\u8C31: embedding-3 \xB7 \u7845\u57FA\u6D41\u52A8: BAAI/bge-m3 \xB7 Ollama: bge-m3",
1467
+ visible: "${data.embedder_provider && data.embedder_provider !== 'none'}"
1468
+ },
1469
+ {
1470
+ type: "text",
1471
+ key: "embedder_base_url",
1472
+ label: "Base URL",
1473
+ required: false,
1474
+ description: "Endpoint root (without /embeddings). Auto-filled from preset; override for proxies or self-hosted gateways.",
1475
+ visible: "${data.embedder_provider === 'custom' || data.embedder_provider === 'azure'}"
1476
+ },
1477
+ {
1478
+ type: "number",
1479
+ key: "embedder_dimensions",
1480
+ label: "Dimensions",
1481
+ required: false,
1482
+ min: 1,
1483
+ max: 8192,
1484
+ description: "Override output dimensionality (Matryoshka models only \u2014 OpenAI v3, \u667A\u8C31 embedding-3, BGE-m3 dense). Leave blank to use the model default.",
1485
+ visible: "${data.embedder_provider && data.embedder_provider !== 'none'}"
1486
+ },
1487
+ {
1488
+ type: "number",
1489
+ key: "embedder_batch_size",
1490
+ label: "Batch size",
1491
+ required: false,
1492
+ default: 64,
1493
+ min: 1,
1494
+ max: 2048,
1495
+ description: "Chunks per embed() call. Reduce if hitting provider rate / size limits.",
1496
+ visible: "${data.embedder_provider && data.embedder_provider !== 'none'}"
1497
+ },
1498
+ {
1499
+ type: "action_button",
1500
+ id: "test_embedder",
1501
+ label: "Test embedder",
1502
+ required: false,
1503
+ icon: "Plug",
1504
+ handler: { kind: "http", method: "POST", url: "/api/settings/ai/test_embedder" }
1505
+ }
1506
+ ]
1507
+ };
1508
+ var aiSettingsManifest = manifest3;
1509
+ var aiTestActionHandler = async ({ values, payload }) => {
1510
+ const overrides = payload && typeof payload === "object" && payload !== null && "values" in payload ? payload.values ?? {} : {};
1511
+ const merged = { ...values, ...overrides };
1512
+ const provider = String(merged.provider ?? "memory");
1513
+ values = merged;
1514
+ if (provider === "memory") {
1515
+ return {
1516
+ ok: true,
1517
+ severity: "warning",
1518
+ message: "Memory provider is an echo stub \u2014 no external call to validate. Switch to a real provider for production."
1519
+ };
1520
+ }
1521
+ if (provider === "gateway") {
1522
+ if (!values.gateway_model) {
1523
+ return { ok: false, severity: "error", message: "Gateway model is required (e.g. openai/gpt-4o)." };
1524
+ }
1525
+ return {
1526
+ ok: true,
1527
+ severity: "info",
1528
+ message: `Vercel AI Gateway configured (model=${values.gateway_model}). Mount @objectstack/service-ai to exercise live calls.`
1529
+ };
1530
+ }
1531
+ const keyField = `${provider}_api_key`;
1532
+ if (!values[keyField]) {
1533
+ return { ok: false, severity: "error", message: `${provider} API key is required.` };
1534
+ }
1535
+ const modelField = `${provider}_model`;
1536
+ const model = values[modelField] ?? "(default)";
1537
+ return {
1538
+ ok: true,
1539
+ severity: "info",
1540
+ message: `${provider} configured (model=${model}). Mount @objectstack/service-ai to exercise live calls.`
1541
+ };
1542
+ };
1543
+
1544
+ // src/manifests/knowledge.manifest.ts
1545
+ var manifest4 = {
1546
+ namespace: "knowledge",
1547
+ version: 1,
1548
+ label: "Knowledge",
1549
+ icon: "BookOpen",
1550
+ description: "Vector-store backend for RAG / knowledge sources. \u26A0 Switching adapter does NOT migrate existing indices \u2014 documents indexed under the previous adapter become unreachable until re-indexed. The embedder is configured separately under AI.",
1551
+ scope: "global",
1552
+ readPermission: "setup.access",
1553
+ writePermission: "setup.write",
1554
+ category: "Infrastructure",
1555
+ order: 35,
1556
+ specifiers: [
1557
+ // ── Adapter selection ─────────────────────────────────────────
1558
+ {
1559
+ type: "group",
1560
+ id: "adapter",
1561
+ label: "Backend",
1562
+ required: false,
1563
+ description: "Choose where document chunks and their vectors are stored."
1564
+ },
1565
+ {
1566
+ type: "select",
1567
+ key: "adapter",
1568
+ label: "Adapter",
1569
+ required: true,
1570
+ default: "memory",
1571
+ options: [
1572
+ { value: "memory", label: "In-memory (dev / test only)" },
1573
+ { value: "turso", label: "Turso / libSQL (cloud or local)" },
1574
+ { value: "ragflow", label: "RAGFlow (external)" }
1575
+ ]
1576
+ },
1577
+ // ── Turso / libSQL ────────────────────────────────────────────
1578
+ {
1579
+ type: "group",
1580
+ id: "turso",
1581
+ label: "Turso / libSQL",
1582
+ required: false,
1583
+ visible: "${data.adapter === 'turso'}",
1584
+ description: "Works against managed Turso (libsql://\u2026), local file (file:./knowledge.db), or in-memory (:memory:). For per-tenant cloud deployments, leave blank to reuse the tenant's primary libSQL connection."
1585
+ },
1586
+ {
1587
+ type: "text",
1588
+ key: "turso_url",
1589
+ label: "Connection URL",
1590
+ required: false,
1591
+ description: "Examples: libsql://your-tenant.turso.io \xB7 file:./.objectstack/knowledge.db \xB7 :memory:",
1592
+ visible: "${data.adapter === 'turso'}"
1593
+ },
1594
+ {
1595
+ type: "password",
1596
+ key: "turso_auth_token",
1597
+ label: "Auth token",
1598
+ required: false,
1599
+ encrypted: true,
1600
+ description: "Only required for managed Turso URLs. Leave blank for local file / :memory:.",
1601
+ visible: "${data.adapter === 'turso'}"
1602
+ },
1603
+ // ── RAGFlow ───────────────────────────────────────────────────
1604
+ {
1605
+ type: "group",
1606
+ id: "ragflow",
1607
+ label: "RAGFlow",
1608
+ required: false,
1609
+ visible: "${data.adapter === 'ragflow'}",
1610
+ description: "External RAGFlow deployment. See https://ragflow.io for self-host instructions."
1611
+ },
1612
+ {
1613
+ type: "text",
1614
+ key: "ragflow_base_url",
1615
+ label: "Base URL",
1616
+ required: true,
1617
+ description: "Example: http://localhost:9380",
1618
+ visible: "${data.adapter === 'ragflow'}"
1619
+ },
1620
+ {
1621
+ type: "password",
1622
+ key: "ragflow_api_key",
1623
+ label: "API key",
1624
+ required: true,
1625
+ encrypted: true,
1626
+ visible: "${data.adapter === 'ragflow'}"
1627
+ },
1628
+ {
1629
+ type: "text",
1630
+ key: "ragflow_default_dataset",
1631
+ label: "Default dataset id",
1632
+ required: false,
1633
+ description: "Used when a KnowledgeSource does not specify its own RAGFlow dataset.",
1634
+ visible: "${data.adapter === 'ragflow'}"
1635
+ },
1636
+ // ── Indexing defaults ─────────────────────────────────────────
1637
+ {
1638
+ type: "group",
1639
+ id: "indexing",
1640
+ label: "Indexing defaults",
1641
+ required: false,
1642
+ description: "Per-source values on KnowledgeSource.adapterConfig take precedence.",
1643
+ visible: "${data.adapter !== 'memory'}"
1644
+ },
1645
+ {
1646
+ type: "number",
1647
+ key: "chunk_target",
1648
+ label: "Target chunk size (chars)",
1649
+ required: false,
1650
+ default: 800,
1651
+ min: 64,
1652
+ max: 8192,
1653
+ description: "Soft cap on chunk size in characters before token-aware splitting kicks in.",
1654
+ visible: "${data.adapter !== 'memory'}"
1655
+ },
1656
+ {
1657
+ type: "number",
1658
+ key: "chunk_overlap",
1659
+ label: "Chunk overlap (chars)",
1660
+ required: false,
1661
+ default: 80,
1662
+ min: 0,
1663
+ max: 2048,
1664
+ description: "Characters retained from the previous chunk so context survives the boundary.",
1665
+ visible: "${data.adapter !== 'memory'}"
1666
+ },
1667
+ {
1668
+ type: "number",
1669
+ key: "over_fetch",
1670
+ label: "Over-fetch multiplier",
1671
+ required: false,
1672
+ default: 4,
1673
+ min: 1,
1674
+ max: 20,
1675
+ description: "Internal `topK * overFetch` candidates fetched so JS-side metadata filtering still has rows to return.",
1676
+ visible: "${data.adapter === 'turso'}"
1677
+ },
1678
+ // ── Permissions ───────────────────────────────────────────────
1679
+ { type: "group", id: "permissions", label: "Permissions", required: false },
1680
+ {
1681
+ type: "toggle",
1682
+ key: "enforce_rls",
1683
+ label: "Enforce RLS on search",
1684
+ required: false,
1685
+ default: true,
1686
+ description: "Re-check every hit against the caller's record-level permissions via IDataEngine. \u26A0 Disabling skips the platform's unique safeguard against vector-store data leakage \u2014 leave on in production."
1687
+ },
1688
+ // ── Probe ─────────────────────────────────────────────────────
1689
+ {
1690
+ type: "action_button",
1691
+ id: "test",
1692
+ label: "Test connection",
1693
+ required: false,
1694
+ icon: "Plug",
1695
+ handler: { kind: "http", method: "POST", url: "/api/settings/knowledge/test" }
1696
+ }
1697
+ ]
1698
+ };
1699
+ var knowledgeSettingsManifest = manifest4;
1700
+
1202
1701
  // src/manifests/index.ts
1203
1702
  var builtinSettingsManifests = [
1204
1703
  brandingSettingsManifest,
1205
1704
  mailSettingsManifest,
1206
1705
  storageSettingsManifest,
1706
+ aiSettingsManifest,
1707
+ knowledgeSettingsManifest,
1207
1708
  featureFlagsSettingsManifest
1208
1709
  ];
1209
1710
 
@@ -1329,6 +1830,196 @@ var en = {
1329
1830
  actions: {
1330
1831
  test: { label: "Test connection" }
1331
1832
  }
1833
+ },
1834
+ ai: {
1835
+ title: "AI & Embedder",
1836
+ description: "LLM provider, model, credentials, and embedder configuration used by the platform AI and knowledge services.",
1837
+ groups: {
1838
+ provider: {
1839
+ title: "Provider",
1840
+ description: "Choose the LLM backend. Memory mode echoes input \u2014 useful for tests but never for production."
1841
+ },
1842
+ gateway: {
1843
+ title: "Vercel AI Gateway",
1844
+ description: "Multi-provider router. The model spec follows `provider/model`, e.g. `openai/gpt-4o`."
1845
+ },
1846
+ openai: { title: "OpenAI" },
1847
+ anthropic: { title: "Anthropic" },
1848
+ google: { title: "Google" },
1849
+ defaults: {
1850
+ title: "Generation defaults",
1851
+ description: "Applied when an agent or chat request does not specify its own value."
1852
+ },
1853
+ observability: { title: "Observability" },
1854
+ embedder: {
1855
+ title: "Embedder",
1856
+ description: "Text \u2192 vector provider used by knowledge sources and RAG. Independent from the chat provider above."
1857
+ }
1858
+ },
1859
+ keys: {
1860
+ provider: {
1861
+ label: "Provider",
1862
+ options: {
1863
+ memory: "Memory (echo \u2014 testing only)",
1864
+ gateway: "Vercel AI Gateway",
1865
+ openai: "OpenAI",
1866
+ anthropic: "Anthropic",
1867
+ google: "Google Generative AI"
1868
+ }
1869
+ },
1870
+ gateway_model: {
1871
+ label: "Gateway model",
1872
+ help: "Forwarded as AI_GATEWAY_MODEL. Example: openai/gpt-4o"
1873
+ },
1874
+ gateway_api_key: {
1875
+ label: "Gateway API key",
1876
+ help: "Optional \u2014 required only if the gateway enforces auth."
1877
+ },
1878
+ openai_api_key: {
1879
+ label: "OpenAI API key",
1880
+ help: "Forwarded as OPENAI_API_KEY. Stored encrypted at rest."
1881
+ },
1882
+ openai_model: {
1883
+ label: "Model",
1884
+ help: "Default model id. Per-agent overrides take precedence."
1885
+ },
1886
+ openai_base_url: {
1887
+ label: "Base URL",
1888
+ help: "Override for Azure OpenAI or self-hosted gateways. Leave blank for api.openai.com."
1889
+ },
1890
+ anthropic_api_key: {
1891
+ label: "Anthropic API key",
1892
+ help: "Forwarded as ANTHROPIC_API_KEY. Stored encrypted at rest."
1893
+ },
1894
+ anthropic_model: { label: "Model" },
1895
+ google_api_key: {
1896
+ label: "Google API key",
1897
+ help: "Forwarded as GOOGLE_GENERATIVE_AI_API_KEY. Stored encrypted at rest."
1898
+ },
1899
+ google_model: { label: "Model" },
1900
+ temperature: {
1901
+ label: "Temperature",
1902
+ help: "0 = deterministic, 2 = highly creative."
1903
+ },
1904
+ max_tokens: {
1905
+ label: "Max output tokens",
1906
+ help: "Hard cap on tokens generated per response."
1907
+ },
1908
+ request_timeout_ms: { label: "Request timeout (ms)" },
1909
+ trace_enabled: {
1910
+ label: "Record traces",
1911
+ help: "Persist prompt/response traces to sys_ai_trace for debugging and replay."
1912
+ },
1913
+ log_prompts: {
1914
+ label: "Log full prompts",
1915
+ help: "Include rendered prompts (not just metadata) in trace rows. \u26A0 May leak PII \u2014 disable in regulated environments."
1916
+ },
1917
+ embedder_provider: {
1918
+ label: "Provider",
1919
+ options: {
1920
+ none: "Disabled (no embeddings)",
1921
+ openai: "OpenAI",
1922
+ azure: "Azure OpenAI",
1923
+ dashscope: "\u963F\u91CC\u901A\u4E49 DashScope",
1924
+ zhipu: "\u667A\u8C31 BigModel",
1925
+ siliconflow: "\u7845\u57FA\u6D41\u52A8 SiliconFlow",
1926
+ doubao: "\u706B\u5C71\u5F15\u64CE Doubao",
1927
+ minimax: "MiniMax",
1928
+ ollama: "Ollama (local)",
1929
+ custom: "Custom (OpenAI-compatible)"
1930
+ }
1931
+ },
1932
+ embedder_api_key: {
1933
+ label: "Embedder API key",
1934
+ help: "Bearer token sent as Authorization header. For Ollama any non-empty value works."
1935
+ },
1936
+ embedder_model: {
1937
+ label: "Model",
1938
+ help: "Examples \u2014 OpenAI: text-embedding-3-small \xB7 \u963F\u91CC\u901A\u4E49: text-embedding-v3 \xB7 \u667A\u8C31: embedding-3 \xB7 \u7845\u57FA\u6D41\u52A8: BAAI/bge-m3 \xB7 Ollama: bge-m3"
1939
+ },
1940
+ embedder_base_url: {
1941
+ label: "Base URL",
1942
+ help: "Endpoint root (without /embeddings). Auto-filled from preset; override for proxies or self-hosted gateways."
1943
+ },
1944
+ embedder_dimensions: {
1945
+ label: "Dimensions",
1946
+ help: "Override output dimensionality (Matryoshka models only). Leave blank to use the model default."
1947
+ },
1948
+ embedder_batch_size: {
1949
+ label: "Batch size",
1950
+ help: "Chunks per embed() call. Reduce if hitting provider rate / size limits."
1951
+ }
1952
+ },
1953
+ actions: {
1954
+ test: { label: "Test connection" },
1955
+ test_embedder: { label: "Test embedder" }
1956
+ }
1957
+ },
1958
+ knowledge: {
1959
+ title: "Knowledge",
1960
+ description: "Vector-store backend for RAG / knowledge sources. \u26A0 Switching adapter does NOT migrate existing indices.",
1961
+ groups: {
1962
+ adapter: {
1963
+ title: "Backend",
1964
+ description: "Choose where document chunks and their vectors are stored."
1965
+ },
1966
+ turso: {
1967
+ title: "Turso / libSQL",
1968
+ description: "Works against managed Turso, local file, or in-memory."
1969
+ },
1970
+ ragflow: {
1971
+ title: "RAGFlow",
1972
+ description: "External RAGFlow deployment. See https://ragflow.io for self-host instructions."
1973
+ },
1974
+ indexing: {
1975
+ title: "Indexing defaults",
1976
+ description: "Per-source values on KnowledgeSource.adapterConfig take precedence."
1977
+ },
1978
+ permissions: { title: "Permissions" }
1979
+ },
1980
+ keys: {
1981
+ adapter: {
1982
+ label: "Adapter",
1983
+ options: {
1984
+ memory: "In-memory (dev / test only)",
1985
+ turso: "Turso / libSQL (cloud or local)",
1986
+ ragflow: "RAGFlow (external)"
1987
+ }
1988
+ },
1989
+ turso_url: {
1990
+ label: "Connection URL",
1991
+ help: "Examples: libsql://your-tenant.turso.io \xB7 file:./.objectstack/knowledge.db \xB7 :memory:"
1992
+ },
1993
+ turso_auth_token: {
1994
+ label: "Auth token",
1995
+ help: "Only required for managed Turso URLs."
1996
+ },
1997
+ ragflow_base_url: { label: "Base URL", help: "Example: http://localhost:9380" },
1998
+ ragflow_api_key: { label: "API key" },
1999
+ ragflow_default_dataset: {
2000
+ label: "Default dataset id",
2001
+ help: "Used when a KnowledgeSource does not specify its own RAGFlow dataset."
2002
+ },
2003
+ chunk_target: {
2004
+ label: "Target chunk size (chars)",
2005
+ help: "Soft cap on chunk size before token-aware splitting kicks in."
2006
+ },
2007
+ chunk_overlap: {
2008
+ label: "Chunk overlap (chars)",
2009
+ help: "Characters retained from the previous chunk so context survives the boundary."
2010
+ },
2011
+ over_fetch: {
2012
+ label: "Over-fetch multiplier",
2013
+ help: "Internal topK \xD7 overFetch candidates fetched so JS-side metadata filtering still has rows."
2014
+ },
2015
+ enforce_rls: {
2016
+ label: "Enforce RLS on search",
2017
+ help: "Re-check every hit against the caller's record-level permissions. \u26A0 Disabling skips the platform's unique safeguard."
2018
+ }
2019
+ },
2020
+ actions: {
2021
+ test: { label: "Test connection" }
2022
+ }
1332
2023
  }
1333
2024
  }
1334
2025
  };
@@ -1455,6 +2146,103 @@ var zhCN = {
1455
2146
  actions: {
1456
2147
  test: { label: "\u6D4B\u8BD5\u8FDE\u63A5" }
1457
2148
  }
2149
+ },
2150
+ ai: {
2151
+ title: "AI \u4E0E Embedder",
2152
+ description: "\u5E73\u53F0 AI \u4E0E\u77E5\u8BC6\u5E93\u670D\u52A1\u4F7F\u7528\u7684 LLM \u63D0\u4F9B\u5546\u3001\u6A21\u578B\u3001\u51ED\u636E\u4E0E\u5411\u91CF\u5316\u914D\u7F6E\u3002",
2153
+ groups: {
2154
+ provider: { title: "\u63D0\u4F9B\u5546", description: "\u9009\u62E9 LLM \u540E\u7AEF\u3002Memory \u6A21\u5F0F\u4EC5\u539F\u6837\u56DE\u663E\u8F93\u5165,\u4EC5\u7528\u4E8E\u6D4B\u8BD5,\u4E25\u7981\u7528\u4E8E\u751F\u4EA7\u3002" },
2155
+ gateway: { title: "Vercel AI Gateway", description: "\u591A\u63D0\u4F9B\u5546\u8DEF\u7531\u5668\u3002\u6A21\u578B\u89C4\u683C\u9075\u5FAA `provider/model` \u683C\u5F0F,\u4F8B\u5982 `openai/gpt-4o`\u3002" },
2156
+ openai: { title: "OpenAI" },
2157
+ anthropic: { title: "Anthropic" },
2158
+ google: { title: "Google" },
2159
+ defaults: { title: "\u751F\u6210\u9ED8\u8BA4\u503C", description: "\u5F53 Agent \u6216\u804A\u5929\u8BF7\u6C42\u672A\u6307\u5B9A\u65F6\u4F7F\u7528\u3002" },
2160
+ observability: { title: "\u53EF\u89C2\u6D4B\u6027" },
2161
+ embedder: { title: "Embedder", description: "\u77E5\u8BC6\u5E93\u548C RAG \u4F7F\u7528\u7684\u6587\u672C\u2192\u5411\u91CF\u63D0\u4F9B\u5546,\u4E0E\u4E0A\u65B9\u804A\u5929\u63D0\u4F9B\u5546\u76F8\u4E92\u72EC\u7ACB\u3002" }
2162
+ },
2163
+ keys: {
2164
+ provider: {
2165
+ label: "\u63D0\u4F9B\u5546",
2166
+ options: {
2167
+ memory: "Memory(\u56DE\u663E \u2014 \u4EC5\u6D4B\u8BD5)",
2168
+ gateway: "Vercel AI Gateway",
2169
+ openai: "OpenAI",
2170
+ anthropic: "Anthropic",
2171
+ google: "Google Generative AI"
2172
+ }
2173
+ },
2174
+ gateway_model: { label: "Gateway \u6A21\u578B", help: "\u4F5C\u4E3A AI_GATEWAY_MODEL \u8F6C\u53D1\u3002\u793A\u4F8B:openai/gpt-4o" },
2175
+ gateway_api_key: { label: "Gateway API Key", help: "\u53EF\u9009 \u2014\u2014 \u4EC5\u5F53 Gateway \u5F3A\u5236\u9274\u6743\u65F6\u9700\u8981\u3002" },
2176
+ openai_api_key: { label: "OpenAI API Key", help: "\u4F5C\u4E3A OPENAI_API_KEY \u8F6C\u53D1,\u52A0\u5BC6\u5B58\u50A8\u3002" },
2177
+ openai_model: { label: "\u6A21\u578B", help: "\u9ED8\u8BA4\u6A21\u578B ID\u3002Agent \u7EA7\u8986\u76D6\u4F18\u5148\u751F\u6548\u3002" },
2178
+ openai_base_url: { label: "Base URL", help: "\u7528\u4E8E Azure OpenAI \u6216\u81EA\u5EFA\u7F51\u5173\u3002\u7559\u7A7A\u8D70 api.openai.com\u3002" },
2179
+ anthropic_api_key: { label: "Anthropic API Key", help: "\u4F5C\u4E3A ANTHROPIC_API_KEY \u8F6C\u53D1,\u52A0\u5BC6\u5B58\u50A8\u3002" },
2180
+ anthropic_model: { label: "\u6A21\u578B" },
2181
+ google_api_key: { label: "Google API Key", help: "\u4F5C\u4E3A GOOGLE_GENERATIVE_AI_API_KEY \u8F6C\u53D1,\u52A0\u5BC6\u5B58\u50A8\u3002" },
2182
+ google_model: { label: "\u6A21\u578B" },
2183
+ temperature: { label: "\u6E29\u5EA6", help: "0 = \u786E\u5B9A\u6027,2 = \u9AD8\u5EA6\u53D1\u6563\u3002" },
2184
+ max_tokens: { label: "\u6700\u5927\u8F93\u51FA tokens", help: "\u5355\u6B21\u54CD\u5E94\u751F\u6210\u7684\u786C\u4E0A\u9650\u3002" },
2185
+ request_timeout_ms: { label: "\u8BF7\u6C42\u8D85\u65F6(\u6BEB\u79D2)" },
2186
+ trace_enabled: { label: "\u8BB0\u5F55 Trace", help: "\u5C06 prompt/response \u843D\u5165 sys_ai_trace,\u4FBF\u4E8E\u8C03\u8BD5\u4E0E\u56DE\u653E\u3002" },
2187
+ log_prompts: { label: "\u8BB0\u5F55\u5B8C\u6574 Prompt", help: "\u5728 trace \u884C\u4E2D\u5305\u542B\u5B8C\u6574 prompt \u800C\u975E\u4EC5\u5143\u6570\u636E\u3002\u26A0 \u53EF\u80FD\u6CC4\u9732 PII,\u5408\u89C4\u573A\u666F\u8BF7\u5173\u95ED\u3002" },
2188
+ embedder_provider: {
2189
+ label: "\u63D0\u4F9B\u5546",
2190
+ options: {
2191
+ none: "\u7981\u7528(\u4E0D\u505A\u5411\u91CF\u5316)",
2192
+ openai: "OpenAI",
2193
+ azure: "Azure OpenAI",
2194
+ dashscope: "\u963F\u91CC\u901A\u4E49 DashScope",
2195
+ zhipu: "\u667A\u8C31 BigModel",
2196
+ siliconflow: "\u7845\u57FA\u6D41\u52A8 SiliconFlow",
2197
+ doubao: "\u706B\u5C71\u5F15\u64CE Doubao",
2198
+ minimax: "MiniMax",
2199
+ ollama: "Ollama(\u672C\u5730)",
2200
+ custom: "\u81EA\u5B9A\u4E49(OpenAI \u517C\u5BB9)"
2201
+ }
2202
+ },
2203
+ embedder_api_key: { label: "Embedder API Key", help: "\u4F5C\u4E3A Authorization Bearer \u53D1\u9001\u3002Ollama \u4EFB\u610F\u975E\u7A7A\u503C\u5747\u53EF\u3002" },
2204
+ embedder_model: { label: "\u6A21\u578B", help: "\u793A\u4F8B \u2014 OpenAI: text-embedding-3-small \xB7 \u963F\u91CC\u901A\u4E49: text-embedding-v3 \xB7 \u667A\u8C31: embedding-3 \xB7 \u7845\u57FA\u6D41\u52A8: BAAI/bge-m3 \xB7 Ollama: bge-m3" },
2205
+ embedder_base_url: { label: "Base URL", help: "\u7AEF\u70B9\u6839\u8DEF\u5F84(\u4E0D\u542B /embeddings)\u3002\u9884\u8BBE\u4F1A\u81EA\u52A8\u586B\u5145,\u53EF\u8986\u76D6\u4E3A\u4EE3\u7406\u6216\u81EA\u5EFA\u7F51\u5173\u3002" },
2206
+ embedder_dimensions: { label: "\u7EF4\u5EA6", help: "\u8986\u76D6\u8F93\u51FA\u7EF4\u5EA6(\u4EC5 Matryoshka \u6A21\u578B\u652F\u6301)\u3002\u7559\u7A7A\u4F7F\u7528\u6A21\u578B\u9ED8\u8BA4\u503C\u3002" },
2207
+ embedder_batch_size: { label: "\u6279\u91CF\u5927\u5C0F", help: "\u5355\u6B21 embed() \u8C03\u7528\u7684 chunk \u6570\u3002\u547D\u4E2D\u901F\u7387/\u5927\u5C0F\u9650\u5236\u65F6\u8C03\u5C0F\u3002" }
2208
+ },
2209
+ actions: {
2210
+ test: { label: "\u6D4B\u8BD5\u8FDE\u63A5" },
2211
+ test_embedder: { label: "\u6D4B\u8BD5 Embedder" }
2212
+ }
2213
+ },
2214
+ knowledge: {
2215
+ title: "\u77E5\u8BC6\u5E93",
2216
+ description: "RAG / \u77E5\u8BC6\u6E90\u4F7F\u7528\u7684\u5411\u91CF\u5B58\u50A8\u540E\u7AEF\u3002\u26A0 \u5207\u6362\u9002\u914D\u5668\u4E0D\u4F1A\u8FC1\u79FB\u5DF2\u6709\u7D22\u5F15\u3002",
2217
+ groups: {
2218
+ adapter: { title: "\u540E\u7AEF", description: "\u9009\u62E9\u6587\u6863\u5206\u5757\u53CA\u5176\u5411\u91CF\u7684\u5B58\u50A8\u4F4D\u7F6E\u3002" },
2219
+ turso: { title: "Turso / libSQL", description: "\u652F\u6301\u6258\u7BA1 Turso\u3001\u672C\u5730\u6587\u4EF6\u3001\u5185\u5B58\u4E09\u79CD\u6A21\u5F0F\u3002" },
2220
+ ragflow: { title: "RAGFlow", description: "\u5916\u90E8 RAGFlow \u90E8\u7F72\u3002\u81EA\u90E8\u7F72\u6587\u6863\u89C1 https://ragflow.io \u3002" },
2221
+ indexing: { title: "\u7D22\u5F15\u9ED8\u8BA4\u503C", description: "KnowledgeSource.adapterConfig \u4E0A\u7684\u9010\u6E90\u8986\u76D6\u4F18\u5148\u751F\u6548\u3002" },
2222
+ permissions: { title: "\u6743\u9650" }
2223
+ },
2224
+ keys: {
2225
+ adapter: {
2226
+ label: "\u9002\u914D\u5668",
2227
+ options: {
2228
+ memory: "\u5185\u5B58(\u4EC5\u5F00\u53D1/\u6D4B\u8BD5)",
2229
+ turso: "Turso / libSQL(\u4E91\u7AEF\u6216\u672C\u5730)",
2230
+ ragflow: "RAGFlow(\u5916\u90E8)"
2231
+ }
2232
+ },
2233
+ turso_url: { label: "\u8FDE\u63A5 URL", help: "\u793A\u4F8B:libsql://your-tenant.turso.io \xB7 file:./.objectstack/knowledge.db \xB7 :memory:" },
2234
+ turso_auth_token: { label: "Auth Token", help: "\u4EC5\u6258\u7BA1 Turso URL \u9700\u8981\u3002" },
2235
+ ragflow_base_url: { label: "Base URL", help: "\u793A\u4F8B:http://localhost:9380" },
2236
+ ragflow_api_key: { label: "API Key" },
2237
+ ragflow_default_dataset: { label: "\u9ED8\u8BA4 Dataset ID", help: "KnowledgeSource \u672A\u6307\u5B9A\u65F6\u4F7F\u7528\u3002" },
2238
+ chunk_target: { label: "\u76EE\u6807 chunk \u5927\u5C0F(\u5B57\u7B26)", help: "\u5728\u6309 token \u5207\u5206\u4E4B\u524D\u7684\u8F6F\u4E0A\u9650\u3002" },
2239
+ chunk_overlap: { label: "Chunk \u91CD\u53E0(\u5B57\u7B26)", help: "\u4FDD\u7559\u4E0A\u4E00\u4E2A chunk \u672B\u5C3E\u7684\u5B57\u7B26,\u4EE5\u4FDD\u8BC1\u8DE8\u754C\u4E0A\u4E0B\u6587\u3002" },
2240
+ over_fetch: { label: "\u8FC7\u53D6\u500D\u6570", help: "\u5185\u90E8\u6309 topK \xD7 overFetch \u62C9\u53D6\u5019\u9009,\u4EE5\u4FBF JS \u7AEF\u5143\u6570\u636E\u8FC7\u6EE4\u4ECD\u6709\u884C\u53EF\u8FD4\u56DE\u3002" },
2241
+ enforce_rls: { label: "\u641C\u7D22\u65F6\u5F3A\u5236 RLS", help: "\u5BF9\u6BCF\u6761\u547D\u4E2D\u901A\u8FC7 IDataEngine \u518D\u6B21\u6821\u9A8C\u8C03\u7528\u65B9\u7684\u884C\u7EA7\u6743\u9650\u3002\u26A0 \u5173\u95ED\u5C06\u8DF3\u8FC7\u5E73\u53F0\u5BF9\u5411\u91CF\u5B58\u50A8\u6570\u636E\u5916\u6CC4\u7684\u72EC\u6709\u9632\u62A4\u3002" }
2242
+ },
2243
+ actions: {
2244
+ test: { label: "\u6D4B\u8BD5\u8FDE\u63A5" }
2245
+ }
1458
2246
  }
1459
2247
  }
1460
2248
  };
@@ -1604,7 +2392,8 @@ var SettingsServicePlugin = class {
1604
2392
  manifests: opts.manifests ?? builtinSettingsManifests,
1605
2393
  actionHandlers: opts.actionHandlers ?? {
1606
2394
  mail: { test: mailTestActionHandler },
1607
- storage: { test: storageTestActionHandler }
2395
+ storage: { test: storageTestActionHandler },
2396
+ ai: { test: aiTestActionHandler }
1608
2397
  }
1609
2398
  };
1610
2399
  }