scream-code 0.8.4 → 0.8.5

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.
@@ -5,7 +5,7 @@ const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import { a as __toESM, i as __require, r as __exportAll, t as __commonJSMin } from "./chunk-apG1qJts.mjs";
7
7
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
8
- import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-CptHxtUY.mjs";
8
+ import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-BHYcdFfg.mjs";
9
9
  import { createRequire } from "node:module";
10
10
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
11
11
  import Jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
@@ -56624,10 +56624,31 @@ function buildEmbeddingText(memo) {
56624
56624
  return `${memo.userNeed} ${memo.approach} ${memo.whatWorked}`;
56625
56625
  }
56626
56626
  /**
56627
+ * Cache of created engines keyed by cacheDir.
56628
+ * Guarantees that all callers sharing the same cacheDir reuse the same engine
56629
+ * instance and the same in-flight model download/load — avoiding duplicate
56630
+ * downloads and file-corruption races when /memory and /knowledge both start
56631
+ * before the model is cached.
56632
+ */
56633
+ const engineCache = /* @__PURE__ */ new Map();
56634
+ /**
56627
56635
  * Create an embedding engine backed by fastembed.
56628
56636
  * Lazily loads the model on first use so startup is not blocked.
56629
- */
56630
- function createFastEmbedEngine() {
56637
+ * Engines are cached by cacheDir so repeated calls with the same cacheDir
56638
+ * return the same instance, sharing model state and download progress.
56639
+ * @param cacheDir Absolute path for model cache (e.g. ~/.scream-code/cache/fastembed).
56640
+ * Defaults to "local_cache" (CWD-relative) if not provided — prefer
56641
+ * passing an explicit path so the cache doesn't duplicate across CWDs.
56642
+ */
56643
+ function createFastEmbedEngine(cacheDir) {
56644
+ const key = cacheDir ?? "";
56645
+ const cached = engineCache.get(key);
56646
+ if (cached !== void 0) return cached;
56647
+ const engine = createFastEmbedEngineImpl(cacheDir);
56648
+ engineCache.set(key, engine);
56649
+ return engine;
56650
+ }
56651
+ function createFastEmbedEngineImpl(cacheDir) {
56631
56652
  let embedder = null;
56632
56653
  let initPromise = null;
56633
56654
  let loadFailed = false;
@@ -56640,7 +56661,7 @@ function createFastEmbedEngine() {
56640
56661
  if (texts.length === 0) return [];
56641
56662
  try {
56642
56663
  if (embedder === null) {
56643
- initPromise ??= loadEmbedder();
56664
+ initPromise ??= loadEmbedder(cacheDir);
56644
56665
  embedder = await initPromise;
56645
56666
  if (embedder === null) {
56646
56667
  loadFailed = true;
@@ -56670,9 +56691,12 @@ function createFastEmbedEngine() {
56670
56691
  return denom === 0 ? 0 : dot / denom;
56671
56692
  },
56672
56693
  async ensureReady() {
56673
- if (embedder !== null) return true;
56694
+ if (embedder !== null) {
56695
+ loadFailed = false;
56696
+ return true;
56697
+ }
56674
56698
  try {
56675
- initPromise ??= loadEmbedder();
56699
+ if (loadFailed || initPromise === null) initPromise = loadEmbedder(cacheDir);
56676
56700
  embedder = await initPromise;
56677
56701
  if (embedder === null) {
56678
56702
  initPromise = null;
@@ -56687,19 +56711,71 @@ function createFastEmbedEngine() {
56687
56711
  }
56688
56712
  };
56689
56713
  }
56690
- async function loadEmbedder() {
56714
+ async function loadEmbedder(cacheDir) {
56691
56715
  try {
56692
56716
  const { FlagEmbedding, EmbeddingModel } = await import("./esm-Du2MwXei.mjs");
56693
- return await FlagEmbedding.init({ model: EmbeddingModel.BGESmallZH });
56717
+ if (cacheDir !== void 0) mkdirSync(cacheDir, { recursive: true });
56718
+ const model = EmbeddingModel.BGESmallZH;
56719
+ const initOpts = cacheDir !== void 0 ? {
56720
+ model,
56721
+ cacheDir
56722
+ } : { model };
56723
+ try {
56724
+ return await FlagEmbedding.init(initOpts);
56725
+ } catch (initError) {
56726
+ const msg = initError instanceof Error ? initError.message : "";
56727
+ if (!/Config file not found|Tokenizer file not found|Tokens map file not found/iu.test(msg)) return null;
56728
+ try {
56729
+ await ensureFastembedModelSidecars(String(model), cacheDir);
56730
+ return await FlagEmbedding.init(initOpts);
56731
+ } catch {
56732
+ return null;
56733
+ }
56734
+ }
56694
56735
  } catch {
56695
56736
  return null;
56696
56737
  }
56697
56738
  }
56739
+ /**
56740
+ * Small config/tokenizer files that fastembed expects alongside model.onnx.
56741
+ * If these are missing (e.g. GCS download partially failed), fastembed throws.
56742
+ * We download them from HuggingFace so the model can load — this covers the
56743
+ * case where the GCS tarball was incomplete but the HF repo is reachable.
56744
+ */
56745
+ const FASTEMBED_SIDECARS = [
56746
+ "config.json",
56747
+ "tokenizer.json",
56748
+ "tokenizer_config.json",
56749
+ "special_tokens_map.json"
56750
+ ];
56751
+ const FASTEMBED_HF_REPOS = { "fast-bge-small-zh-v1.5": "BAAI/bge-small-zh-v1.5" };
56752
+ async function ensureFastembedModelSidecars(model, cacheDir) {
56753
+ const repo = FASTEMBED_HF_REPOS[model];
56754
+ if (repo === void 0) return;
56755
+ const modelDir = join(cacheDir ?? "local_cache", model);
56756
+ mkdirSync(modelDir, { recursive: true });
56757
+ for (const fileName of FASTEMBED_SIDECARS) {
56758
+ const target = join(modelDir, fileName);
56759
+ try {
56760
+ const { access } = await import("node:fs/promises");
56761
+ await access(target);
56762
+ continue;
56763
+ } catch {}
56764
+ const hfUrl = `https://huggingface.co/${repo}/resolve/main/${fileName}`;
56765
+ try {
56766
+ const response = await fetch(hfUrl);
56767
+ if (!response.ok) continue;
56768
+ const { writeFile } = await import("node:fs/promises");
56769
+ await writeFile(target, Buffer.from(await response.arrayBuffer()));
56770
+ } catch {}
56771
+ }
56772
+ }
56698
56773
  //#endregion
56699
56774
  //#region ../../packages/memory/src/store.ts
56700
56775
  const FILE_NAME = "entries.jsonl";
56701
56776
  const MIGRATION_MARKER = ".migrated";
56702
56777
  const SQLITE_MIGRATION_MARKER = ".migrated-to-sqlite";
56778
+ const EMBEDDING_LOAD_RETRY_MS = 300 * 1e3;
56703
56779
  var MemoryMemoStore = class MemoryMemoStore {
56704
56780
  projectDir;
56705
56781
  jsonlPath;
@@ -56714,6 +56790,7 @@ var MemoryMemoStore = class MemoryMemoStore {
56714
56790
  embeddingDegraded = false;
56715
56791
  embeddingConsecutiveFailures = 0;
56716
56792
  lastEmbeddingError;
56793
+ embeddingLoadTimer;
56717
56794
  log;
56718
56795
  constructor(projectDir, log) {
56719
56796
  this.projectDir = projectDir;
@@ -56751,6 +56828,10 @@ var MemoryMemoStore = class MemoryMemoStore {
56751
56828
  }
56752
56829
  /** Close the database handle and checkpoint WAL. */
56753
56830
  close() {
56831
+ if (this.embeddingLoadTimer !== void 0) {
56832
+ clearInterval(this.embeddingLoadTimer);
56833
+ this.embeddingLoadTimer = void 0;
56834
+ }
56754
56835
  if (this.db !== void 0) {
56755
56836
  this.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
56756
56837
  this.db.close();
@@ -57128,6 +57209,34 @@ var MemoryMemoStore = class MemoryMemoStore {
57128
57209
  /** Set the embedding engine. Call once after construction, before any writes. */
57129
57210
  setEmbeddingEngine(engine) {
57130
57211
  this.embeddingEngine = engine;
57212
+ this.startEmbeddingLoadLoop();
57213
+ }
57214
+ /**
57215
+ * Background loop that retries model loading every 5 minutes until success.
57216
+ * Covers the case where the model download fails on first use (network issue,
57217
+ * GCS blocked, etc.) — without this, the engine stays loadFailed forever and
57218
+ * all future memos fall back to keyword search silently.
57219
+ * Stops itself once the model loads successfully.
57220
+ */
57221
+ startEmbeddingLoadLoop() {
57222
+ if (this.embeddingEngine === void 0) return;
57223
+ if (this.embeddingLoadTimer !== void 0) return;
57224
+ const attempt = async () => {
57225
+ if (this.embeddingEngine === void 0) return;
57226
+ if (await this.embeddingEngine.ensureReady()) {
57227
+ if (this.embeddingLoadTimer !== void 0) {
57228
+ clearInterval(this.embeddingLoadTimer);
57229
+ this.embeddingLoadTimer = void 0;
57230
+ }
57231
+ this.embeddingDegraded = false;
57232
+ return;
57233
+ }
57234
+ };
57235
+ this.embeddingLoadTimer = setInterval(() => {
57236
+ attempt();
57237
+ }, EMBEDDING_LOAD_RETRY_MS);
57238
+ this.embeddingLoadTimer.unref?.();
57239
+ attempt();
57131
57240
  }
57132
57241
  /** Check whether the store has any vector embeddings. */
57133
57242
  hasEmbeddings() {
@@ -58232,7 +58341,7 @@ var KnowledgeLookupTool = class {
58232
58341
  const llm = { generate: async (systemPrompt, userPrompt) => {
58233
58342
  return this.agent.generateText(systemPrompt, userPrompt);
58234
58343
  } };
58235
- const { multiSearchWithTrace } = await import("./src-DhT3eU2C.mjs");
58344
+ const { multiSearchWithTrace } = await import("./src-CC8SSs-H.mjs");
58236
58345
  const { results, trace } = await multiSearchWithTrace(store, llm, query, { topK });
58237
58346
  if (results.length === 0) return {
58238
58347
  isError: false,
@@ -98593,6 +98702,7 @@ var Agent = class {
98593
98702
  dreamTracker;
98594
98703
  replayBuilder;
98595
98704
  lastLlmConfigLogSignature;
98705
+ sharedEmbeddingEngine;
98596
98706
  constructor(options) {
98597
98707
  this.type = options.type ?? "main";
98598
98708
  this.jian = options.jian;
@@ -98607,6 +98717,8 @@ var Agent = class {
98607
98717
  this.subagentHost = options.subagentHost;
98608
98718
  this.mcp = options.mcp;
98609
98719
  this.hooks = options.hookEngine;
98720
+ const embedCacheDir = options.screamHomeDir !== void 0 ? join$1(options.screamHomeDir, "cache", "fastembed") : void 0;
98721
+ this.sharedEmbeddingEngine = createFastEmbedEngine(embedCacheDir);
98610
98722
  this.log = options.log ?? log;
98611
98723
  this.blobStore = options.homedir ? new BlobStore({ blobsDir: join$1(options.homedir, "blobs") }) : void 0;
98612
98724
  this.records = new AgentRecords(this, options.persistence ?? (options.homedir ? new FileSystemAgentRecordPersistence(join$1(options.homedir, "wire.jsonl"), {
@@ -98665,7 +98777,7 @@ var Agent = class {
98665
98777
  this.log.error("memory legacy migration failed", error);
98666
98778
  }
98667
98779
  try {
98668
- this.memoStore.setEmbeddingEngine(createFastEmbedEngine());
98780
+ this.memoStore.setEmbeddingEngine(this.sharedEmbeddingEngine);
98669
98781
  } catch (error) {
98670
98782
  this.log.warn("embedding engine init failed; falling back to keyword search", error);
98671
98783
  }
@@ -98680,7 +98792,7 @@ var Agent = class {
98680
98792
  this.log.error("knowledge store init failed", error);
98681
98793
  }
98682
98794
  try {
98683
- store.setEmbeddingEngine(createFastEmbedEngine());
98795
+ store.setEmbeddingEngine(this.sharedEmbeddingEngine);
98684
98796
  } catch (error) {
98685
98797
  this.log.warn("knowledge embedding engine init failed", error);
98686
98798
  }
@@ -122269,7 +122381,7 @@ function optionalBuildString(value) {
122269
122381
  return typeof value === "string" && value.length > 0 ? value : void 0;
122270
122382
  }
122271
122383
  const SCREAM_BUILD_INFO = {
122272
- version: optionalBuildString("0.8.4"),
122384
+ version: optionalBuildString("0.8.5"),
122273
122385
  channel: optionalBuildString(""),
122274
122386
  commit: optionalBuildString(""),
122275
122387
  buildTarget: optionalBuildString("darwin-arm64")
@@ -133109,11 +133221,16 @@ function openUrl(url) {
133109
133221
  //#endregion
133110
133222
  //#region src/tui/commands/knowledge-store.ts
133111
133223
  let knowledgeStoreInstance;
133224
+ function getEmbeddingCacheDir() {
133225
+ const dir = join(getDataDir(), "cache", "fastembed");
133226
+ mkdirSync(dir, { recursive: true });
133227
+ return dir;
133228
+ }
133112
133229
  async function getKnowledgeStore() {
133113
133230
  if (knowledgeStoreInstance === void 0) {
133114
133231
  knowledgeStoreInstance = new KnowledgeStore(getDataDir());
133115
133232
  await knowledgeStoreInstance.init();
133116
- knowledgeStoreInstance.setEmbeddingEngine(createFastEmbedEngine());
133233
+ knowledgeStoreInstance.setEmbeddingEngine(createFastEmbedEngine(getEmbeddingCacheDir()));
133117
133234
  }
133118
133235
  return knowledgeStoreInstance;
133119
133236
  }
@@ -133583,7 +133700,7 @@ function serveHTML(res) {
133583
133700
  });
133584
133701
  res.end(HTML);
133585
133702
  }
133586
- async function handleWeb() {
133703
+ async function handleWeb(host) {
133587
133704
  const store = await getKnowledgeStore();
133588
133705
  const s = await store.stats();
133589
133706
  if (s.entities === 0 && s.events === 0) throw new Error("知识库为空,请先用 /knowledge 摄入文档");
@@ -133604,7 +133721,7 @@ async function handleWeb() {
133604
133721
  });
133605
133722
  const url = `http://127.0.0.1:${server.address().port}`;
133606
133723
  openUrl(url);
133607
- console.log(`\n 知识图谱: ${url}\n 按 Ctrl+C 停止服务器\n`);
133724
+ host.showStatus(`知识图谱已打开: ${url}`);
133608
133725
  }
133609
133726
  //#endregion
133610
133727
  //#region src/tui/components/dialogs/knowledge-result-viewer.ts
@@ -134000,6 +134117,7 @@ function makeLlmCaller(host) {
134000
134117
  }
134001
134118
  function formatProgress(progress) {
134002
134119
  switch (progress.stage) {
134120
+ case "embedding-check": return progress.message;
134003
134121
  case "chunking": return `切分文件中...`;
134004
134122
  case "embedding-chunks": return `嵌入 chunks: ${progress.chunkIndex}/${progress.totalChunks}`;
134005
134123
  case "extracting": return `抽取事件: ${progress.chunkIndex}/${progress.totalChunks}`;
@@ -134030,19 +134148,6 @@ async function handleIngest(host) {
134030
134148
  }
134031
134149
  const store = await getKnowledgeStore();
134032
134150
  const llm = makeLlmCaller(host);
134033
- const checkSpinner = host.showProgressSpinner("检查向量模型...");
134034
- if (!(await store.ensureEmbeddingReady()).ok) {
134035
- checkSpinner.stop({
134036
- ok: false,
134037
- label: "向量模型下载失败"
134038
- });
134039
- host.showNotice("向量模型下载失败", "知识库需要中文向量模型 bge-small-zh-v1.5(约 95MB)才能进行语义检索。\n\n可能原因:\n• 网络不通,无法下载模型文件\n• onnxruntime native binding 加载失败\n\n请检查网络后重试。");
134040
- return;
134041
- }
134042
- checkSpinner.stop({
134043
- ok: true,
134044
- label: "向量模型就绪"
134045
- });
134046
134151
  const spinner = host.showProgressSpinner("开始摄入...");
134047
134152
  try {
134048
134153
  if (stats.isDirectory()) {
@@ -134145,11 +134250,17 @@ async function handleSearch(host) {
134145
134250
  ok: true,
134146
134251
  label: "搜索完成"
134147
134252
  });
134253
+ const engine = store.getEmbeddingEngine();
134254
+ const degraded = engine === void 0 || !engine.available;
134148
134255
  if (results.length === 0) {
134149
- await showResultViewer(host, "搜索结果", `查询 "${query}" 未命中任何 chunk`);
134256
+ await showResultViewer(host, "搜索结果", `查询 "${query}" 未命中任何 chunk${degraded ? "\n\n⚠️ 向量模型未就绪,当前为关键词检索模式。如下载失败,建议开启科学上网后重启。" : ""}`);
134150
134257
  return;
134151
134258
  }
134152
134259
  const lines = [`查询: ${query}`, ""];
134260
+ if (degraded) {
134261
+ lines.push("⚠️ 向量模型未就绪,当前为关键词检索模式。如下载失败,建议开启科学上网后重启。");
134262
+ lines.push("");
134263
+ }
134153
134264
  for (const [i, r] of results.entries()) {
134154
134265
  lines.push(`#${i + 1} [score=${r.score.toFixed(3)}] ${r.heading ?? "(无标题)"}`);
134155
134266
  lines.push(` 来源: ${r.sourceName}`);
@@ -134300,7 +134411,7 @@ async function handleKnowledgeCommand(host, _args) {
134300
134411
  else if (value === "search") await handleSearch(host);
134301
134412
  else if (value === "delete") await handleDelete(host);
134302
134413
  else if (value === "stats") await handleStats(host);
134303
- else if (value === "web") await handleWeb();
134414
+ else if (value === "web") await handleWeb(host);
134304
134415
  } catch (error) {
134305
134416
  const msg = error instanceof Error ? error.message : String(error);
134306
134417
  host.showError(`操作失败: ${msg}`);
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  //#region src/main.ts
8
8
  try {
9
- (await import("./app-DYDC5U75.mjs")).main();
9
+ (await import("./app-DFMT0YIn.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
@@ -1392,6 +1392,19 @@ Only include entities that explicitly appear in the query. If no clear entities,
1392
1392
  }
1393
1393
  //#endregion
1394
1394
  //#region ../../packages/knowledge/src/ingest.ts
1395
+ /**
1396
+ * Embed texts, returning null-array if embeddings are skipped or on failure.
1397
+ * Caller should set skipEmbedding=true when the model didn't load in time,
1398
+ * so the entire ingestion is consistent (all or nothing for embeddings).
1399
+ * Search falls back to FTS5 keyword match for chunks without embeddings.
1400
+ */
1401
+ async function embedOrNulls(engine, texts, skipEmbedding) {
1402
+ if (engine === void 0 || texts.length === 0 || skipEmbedding) return texts.map(() => null);
1403
+ if (!engine.available) return texts.map(() => null);
1404
+ const vectors = await engine.embedBatch(texts);
1405
+ if (vectors === null || vectors.length !== texts.length) return texts.map(() => null);
1406
+ return vectors;
1407
+ }
1395
1408
  /** LLM concurrency for extraction — kept low to avoid rate-limit bursts. */
1396
1409
  const LLM_CONCURRENCY = 3;
1397
1410
  const SUPPORTED_EXTENSIONS = new Set([
@@ -1454,12 +1467,25 @@ async function mapWithConcurrency(items, limit, fn, onProgress) {
1454
1467
  */
1455
1468
  async function ingestFile(store, llm, filePath, onProgress) {
1456
1469
  const engine = store.getEmbeddingEngine();
1457
- if (engine === void 0 || !engine.available) {
1470
+ let skipEmbed = engine === void 0;
1471
+ if (engine !== void 0) {
1458
1472
  onProgress?.({
1459
- stage: "error",
1460
- message: "向量模型未就绪"
1473
+ stage: "embedding-check",
1474
+ message: "检查向量模型..."
1461
1475
  });
1462
- throw new Error("向量模型未就绪,请检查网络后重试");
1476
+ try {
1477
+ skipEmbed = !await Promise.race([engine.ensureReady(), new Promise((resolve) => setTimeout(() => resolve(false), 15e3))]);
1478
+ if (skipEmbed) onProgress?.({
1479
+ stage: "embedding-check",
1480
+ message: "向量模型未就绪,本次摄入跳过向量嵌入"
1481
+ });
1482
+ } catch {
1483
+ skipEmbed = true;
1484
+ onProgress?.({
1485
+ stage: "embedding-check",
1486
+ message: "向量模型未就绪,本次摄入跳过向量嵌入"
1487
+ });
1488
+ }
1463
1489
  }
1464
1490
  const existing = await store.findSourceByFilePath(filePath);
1465
1491
  if (existing !== void 0) {
@@ -1501,14 +1527,7 @@ async function ingestFile(store, llm, filePath, onProgress) {
1501
1527
  totalChunks: sections.length,
1502
1528
  message: `嵌入 chunks: 0/${sections.length}`
1503
1529
  });
1504
- const chunkEmbeddings = await engine.embedBatch(sections.map((s) => s.heading !== null ? `${s.heading}\n${s.content}` : s.content));
1505
- if (chunkEmbeddings === null) {
1506
- onProgress?.({
1507
- stage: "error",
1508
- message: "chunk 向量嵌入失败"
1509
- });
1510
- throw new Error("chunk 向量嵌入失败");
1511
- }
1530
+ const chunkEmbeddings = await embedOrNulls(engine, sections.map((s) => s.heading !== null ? `${s.heading}\n${s.content}` : s.content), skipEmbed);
1512
1531
  const chunks = [];
1513
1532
  for (let i = 0; i < sections.length; i++) {
1514
1533
  const section = sections[i];
@@ -1552,14 +1571,7 @@ async function ingestFile(store, llm, filePath, onProgress) {
1552
1571
  });
1553
1572
  const titleTexts = extractedEvents.map((e) => e.title);
1554
1573
  const contentTexts = extractedEvents.map((e) => `${e.title}\n\n${e.content}`);
1555
- const [titleEmbeddings, contentEmbeddings] = await Promise.all([engine.embedBatch(titleTexts), engine.embedBatch(contentTexts)]);
1556
- if (titleEmbeddings === null || contentEmbeddings === null) {
1557
- onProgress?.({
1558
- stage: "error",
1559
- message: "event 向量嵌入失败"
1560
- });
1561
- throw new Error("event 向量嵌入失败");
1562
- }
1574
+ const [titleEmbeddings, contentEmbeddings] = await Promise.all([embedOrNulls(engine, titleTexts, skipEmbed), embedOrNulls(engine, contentTexts, skipEmbed)]);
1563
1575
  const events = [];
1564
1576
  for (let i = 0; i < extractedEvents.length; i++) {
1565
1577
  const extracted = extractedEvents[i];
@@ -1595,18 +1607,11 @@ async function ingestFile(store, llm, filePath, onProgress) {
1595
1607
  if (!entityMap.has(key)) entityMap.set(key, entity);
1596
1608
  }
1597
1609
  const uniqueEntities = Array.from(entityMap.values());
1598
- const entityEmbeddings = uniqueEntities.length > 0 ? await engine.embedBatch(uniqueEntities.map((e) => e.name)) : [];
1599
- if (uniqueEntities.length > 0 && entityEmbeddings === null) {
1600
- onProgress?.({
1601
- stage: "error",
1602
- message: "entity 向量嵌入失败"
1603
- });
1604
- throw new Error("entity 向量嵌入失败");
1605
- }
1610
+ const entityEmbeddings = uniqueEntities.length > 0 ? await embedOrNulls(engine, uniqueEntities.map((e) => e.name), skipEmbed) : [];
1606
1611
  const entityIdByEntityKey = /* @__PURE__ */ new Map();
1607
1612
  for (let i = 0; i < uniqueEntities.length; i++) {
1608
1613
  const entity = uniqueEntities[i];
1609
- const embedding = entityEmbeddings?.[i] ?? null;
1614
+ const embedding = entityEmbeddings[i] ?? null;
1610
1615
  const stored = await store.upsertEntity({
1611
1616
  sourceId: source.id,
1612
1617
  type: entity.type,
@@ -1632,14 +1637,7 @@ async function ingestFile(store, llm, filePath, onProgress) {
1632
1637
  const event = events[eventIndex];
1633
1638
  return entity.description.length > 0 ? entity.description : `${event.title} ${entity.name}`;
1634
1639
  });
1635
- const relationEmbeddings = relationPairs.length > 0 ? await engine.embedBatch(relationTexts) : [];
1636
- if (relationPairs.length > 0 && relationEmbeddings === null) {
1637
- onProgress?.({
1638
- stage: "error",
1639
- message: "relation 向量嵌入失败"
1640
- });
1641
- throw new Error("relation 向量嵌入失败");
1642
- }
1640
+ const relationEmbeddings = relationPairs.length > 0 ? await embedOrNulls(engine, relationTexts, skipEmbed) : [];
1643
1641
  for (let i = 0; i < relationPairs.length; i++) {
1644
1642
  const pair = relationPairs[i];
1645
1643
  const event = events[pair.eventIndex];
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
3
3
  import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
- import { n as multiSearchWithTrace } from "./src-CptHxtUY.mjs";
6
+ import { n as multiSearchWithTrace } from "./src-BHYcdFfg.mjs";
7
7
  export { multiSearchWithTrace };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",