@absolutejs/absolute 0.19.0-beta.497 → 0.19.0-beta.498

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/ai/index.js CHANGED
@@ -4979,6 +4979,7 @@ var ragChat = (config) => {
4979
4979
  const ingestJobs = [];
4980
4980
  const adminActions = [];
4981
4981
  const adminJobs = [];
4982
+ const syncJobs = [];
4982
4983
  const workflowRenderConfig = typeof config.htmx === "object" ? config.htmx.workflowRender ?? config.htmx.workflow?.render : undefined;
4983
4984
  const workflowRenderers = resolveRAGWorkflowRenderers(workflowRenderConfig);
4984
4985
  const createIngestJob = (inputKind, requestedCount) => {
@@ -5012,13 +5013,14 @@ var ragChat = (config) => {
5012
5013
  job.error = error;
5013
5014
  job.extractorNames = extractorNames;
5014
5015
  };
5015
- const createAdminAction = (action, documentId) => {
5016
+ const createAdminAction = (action, documentId, target) => {
5016
5017
  const record = {
5017
5018
  action,
5018
5019
  documentId,
5019
5020
  id: generateId(),
5020
5021
  startedAt: Date.now(),
5021
- status: "completed"
5022
+ status: "completed",
5023
+ target
5022
5024
  };
5023
5025
  adminActions.unshift(record);
5024
5026
  if (adminActions.length > MAX_ADMIN_ACTIONS) {
@@ -5026,7 +5028,7 @@ var ragChat = (config) => {
5026
5028
  }
5027
5029
  return record;
5028
5030
  };
5029
- const createAdminJob = (action, target) => {
5031
+ const createAdminJob = (action, target, bucket = adminJobs) => {
5030
5032
  const job = {
5031
5033
  action,
5032
5034
  id: generateId(),
@@ -5034,9 +5036,9 @@ var ragChat = (config) => {
5034
5036
  status: "running",
5035
5037
  target
5036
5038
  };
5037
- adminJobs.unshift(job);
5038
- if (adminJobs.length > MAX_ADMIN_JOBS) {
5039
- adminJobs.length = MAX_ADMIN_JOBS;
5039
+ bucket.unshift(job);
5040
+ if (bucket.length > MAX_ADMIN_JOBS) {
5041
+ bucket.length = MAX_ADMIN_JOBS;
5040
5042
  }
5041
5043
  return job;
5042
5044
  };
@@ -5066,6 +5068,12 @@ var ragChat = (config) => {
5066
5068
  job.elapsedMs = finishedAt - job.startedAt;
5067
5069
  job.error = error;
5068
5070
  };
5071
+ const buildSyncSources = async () => {
5072
+ if (!indexManager?.listSyncSources) {
5073
+ return [];
5074
+ }
5075
+ return await indexManager.listSyncSources();
5076
+ };
5069
5077
  const toHTMXResponse = (html, status, extraHeaders) => new Response(html, {
5070
5078
  headers: {
5071
5079
  ...HTML_HEADERS,
@@ -5501,6 +5509,12 @@ var ragChat = (config) => {
5501
5509
  }
5502
5510
  failuresByAdminAction.set(job.action, (failuresByAdminAction.get(job.action) ?? 0) + 1);
5503
5511
  }
5512
+ for (const job of syncJobs) {
5513
+ if (job.status !== "failed") {
5514
+ continue;
5515
+ }
5516
+ failuresByAdminAction.set(job.action, (failuresByAdminAction.get(job.action) ?? 0) + 1);
5517
+ }
5504
5518
  return {
5505
5519
  averageChunksPerDocument: documents.length > 0 ? Number((documents.reduce((sum, document) => sum + (document.chunkCount ?? 0), 0) / documents.length).toFixed(2)) : 0,
5506
5520
  duplicateDocumentIds: [...documentIdCounts.entries()].filter(([, count]) => count > 1).map(([id]) => id),
@@ -5546,10 +5560,13 @@ var ragChat = (config) => {
5546
5560
  canClearIndex: Boolean(ragStore?.clear),
5547
5561
  canCreateDocument: Boolean(indexManager?.createDocument),
5548
5562
  canDeleteDocument: Boolean(indexManager?.deleteDocument),
5563
+ canListSyncSources: Boolean(indexManager?.listSyncSources),
5549
5564
  canReindexDocument: Boolean(indexManager?.reindexDocument),
5550
5565
  canReindexSource: Boolean(indexManager?.reindexSource),
5551
5566
  canReseed: Boolean(indexManager?.reseed),
5552
- canReset: Boolean(indexManager?.reset)
5567
+ canReset: Boolean(indexManager?.reset),
5568
+ canSyncAllSources: Boolean(indexManager?.syncAllSources),
5569
+ canSyncSource: Boolean(indexManager?.syncSource)
5553
5570
  });
5554
5571
  const buildOperationsPayload = async () => {
5555
5572
  const collection = config.collection ?? (ragStore ? createRAGCollection({
@@ -5562,14 +5579,15 @@ var ragChat = (config) => {
5562
5579
  return {
5563
5580
  admin: buildAdminCapabilities(),
5564
5581
  adminActions: [...adminActions],
5565
- adminJobs: [...adminJobs],
5582
+ adminJobs: [...adminJobs, ...syncJobs].sort((left, right) => right.startedAt - left.startedAt),
5566
5583
  capabilities: collection?.getCapabilities?.(),
5567
5584
  documents: indexManager ? summarizeDocuments(indexedDocuments) : undefined,
5568
5585
  health: await summarizeHealth(indexedDocuments),
5569
5586
  ingestJobs: [...ingestJobs],
5570
5587
  ok: true,
5571
5588
  readiness: buildReadiness(),
5572
- status: collection?.getStatus?.()
5589
+ status: collection?.getStatus?.(),
5590
+ syncSources: await buildSyncSources()
5573
5591
  };
5574
5592
  };
5575
5593
  const handleStatus = async () => buildOperationsPayload();
@@ -5806,6 +5824,81 @@ var ragChat = (config) => {
5806
5824
  ...normalized
5807
5825
  };
5808
5826
  };
5827
+ const handleSyncSources = async () => {
5828
+ if (!indexManager?.listSyncSources) {
5829
+ return {
5830
+ error: "RAG source sync is not configured",
5831
+ ok: false
5832
+ };
5833
+ }
5834
+ return {
5835
+ ok: true,
5836
+ sources: await indexManager.listSyncSources()
5837
+ };
5838
+ };
5839
+ const handleSyncAllSources = async () => {
5840
+ if (!indexManager?.syncAllSources) {
5841
+ return {
5842
+ error: "RAG source sync is not configured",
5843
+ ok: false
5844
+ };
5845
+ }
5846
+ const job = createAdminJob("sync_all_sources", undefined, syncJobs);
5847
+ try {
5848
+ const result = await indexManager.syncAllSources();
5849
+ const action = createAdminAction("sync_all_sources");
5850
+ completeAdminJob(job);
5851
+ completeAdminAction(action);
5852
+ if (result && "ok" in result) {
5853
+ return result;
5854
+ }
5855
+ return {
5856
+ ok: true,
5857
+ sources: await buildSyncSources()
5858
+ };
5859
+ } catch (caught) {
5860
+ const message = caught instanceof Error ? caught.message : String(caught);
5861
+ failAdminJob(job, message);
5862
+ const action = createAdminAction("sync_all_sources");
5863
+ failAdminAction(action, message);
5864
+ throw caught;
5865
+ }
5866
+ };
5867
+ const handleSyncSource = async (id) => {
5868
+ if (!indexManager?.syncSource) {
5869
+ return {
5870
+ error: "RAG source sync is not configured",
5871
+ ok: false
5872
+ };
5873
+ }
5874
+ if (!id) {
5875
+ return {
5876
+ error: "sync source id is required",
5877
+ ok: false
5878
+ };
5879
+ }
5880
+ const job = createAdminJob("sync_source", id, syncJobs);
5881
+ try {
5882
+ const result = await indexManager.syncSource(id);
5883
+ const action = createAdminAction("sync_source", undefined, id);
5884
+ completeAdminJob(job);
5885
+ completeAdminAction(action);
5886
+ if (result && "ok" in result) {
5887
+ return result;
5888
+ }
5889
+ const source = (await buildSyncSources()).find((record) => record.id === id);
5890
+ return source ? { ok: true, source } : {
5891
+ error: "sync source not found",
5892
+ ok: false
5893
+ };
5894
+ } catch (caught) {
5895
+ const message = caught instanceof Error ? caught.message : String(caught);
5896
+ failAdminJob(job, message);
5897
+ const action = createAdminAction("sync_source", undefined, id);
5898
+ failAdminAction(action, message);
5899
+ throw caught;
5900
+ }
5901
+ };
5809
5902
  const htmxRoutes = () => {
5810
5903
  if (!config.htmx) {
5811
5904
  return new Elysia2;
@@ -6042,6 +6135,51 @@ var ragChat = (config) => {
6042
6135
  set.status = HTTP_STATUS_NOT_FOUND;
6043
6136
  }
6044
6137
  return result;
6138
+ }).get(`${path}/sync`, async ({ request, set }) => {
6139
+ const result = await handleSyncSources();
6140
+ if (!result.ok) {
6141
+ set.status = HTTP_STATUS_NOT_FOUND;
6142
+ }
6143
+ if (config.htmx && isHTMXRequest(request)) {
6144
+ if (!result.ok) {
6145
+ return toHTMXResponse(workflowRenderers.error(result.error), getNumericStatus(set.status));
6146
+ }
6147
+ return toHTMXResponse(workflowRenderers.mutationResult({
6148
+ ok: true,
6149
+ status: `loaded ${"sources" in result ? result.sources.length : 1} sync sources`
6150
+ }));
6151
+ }
6152
+ return result;
6153
+ }).post(`${path}/sync`, async ({ request, set }) => {
6154
+ const result = await handleSyncAllSources();
6155
+ if (!result.ok) {
6156
+ set.status = HTTP_STATUS_NOT_FOUND;
6157
+ }
6158
+ if (config.htmx && isHTMXRequest(request)) {
6159
+ const html = result.ok ? workflowRenderers.mutationResult({
6160
+ ok: true,
6161
+ status: "source sync started and completed successfully"
6162
+ }) : workflowRenderers.error(result.error ?? "Failed to sync sources");
6163
+ return toHTMXResponse(html, getNumericStatus(set.status), {
6164
+ "HX-Trigger": "rag:mutated"
6165
+ });
6166
+ }
6167
+ return result;
6168
+ }).post(`${path}/sync/:id`, async ({ params, request, set }) => {
6169
+ const result = await handleSyncSource(typeof params.id === "string" ? params.id.trim() : "");
6170
+ if (!result.ok) {
6171
+ set.status = result.error === "sync source id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
6172
+ }
6173
+ if (config.htmx && isHTMXRequest(request)) {
6174
+ const html = result.ok ? workflowRenderers.mutationResult({
6175
+ ok: true,
6176
+ status: "source sync started and completed successfully"
6177
+ }) : workflowRenderers.error(result.error ?? "Failed to sync source");
6178
+ return toHTMXResponse(html, getNumericStatus(set.status), {
6179
+ "HX-Trigger": "rag:mutated"
6180
+ });
6181
+ }
6182
+ return result;
6045
6183
  }).post(`${path}/ingest`, async ({ body, request, set }) => {
6046
6184
  const result = await handleIngest(body);
6047
6185
  if (!result.ok) {
@@ -7020,6 +7158,200 @@ var resolveRAGStreamStage = ({
7020
7158
  }
7021
7159
  return "streaming";
7022
7160
  };
7161
+ // src/ai/rag/sync.ts
7162
+ import { createHash } from "crypto";
7163
+ var toSyncError = (caught) => caught instanceof Error ? caught.message : String(caught);
7164
+ var createSyncFingerprint = (document) => createHash("sha1").update(document.source ?? "").update(`
7165
+ `).update(document.title ?? "").update(`
7166
+ `).update(document.text).digest("hex");
7167
+ var toManagedSyncDocument = (sourceId, document, syncKey) => ({
7168
+ ...document,
7169
+ metadata: {
7170
+ ...document.metadata ?? {},
7171
+ syncFingerprint: createSyncFingerprint(document),
7172
+ syncKey,
7173
+ syncSourceId: sourceId
7174
+ }
7175
+ });
7176
+ var isManagedBySyncSource = (document, sourceId) => document.metadata?.syncSourceId === sourceId;
7177
+ var getDocumentSyncFingerprint = (document) => typeof document.metadata?.syncFingerprint === "string" ? document.metadata.syncFingerprint : undefined;
7178
+ var reconcileManagedDocuments = async (input) => {
7179
+ const prepared = prepareRAGDocuments({
7180
+ documents: input.documents
7181
+ });
7182
+ const nextDocumentIds = new Set(prepared.map((document) => document.documentId));
7183
+ const nextFingerprintById = new Map(prepared.map((document, index) => [
7184
+ document.documentId,
7185
+ createSyncFingerprint(input.documents[index])
7186
+ ]));
7187
+ const existingDocuments = input.listDocuments ? await input.listDocuments() : [];
7188
+ const managedDocuments = existingDocuments.filter((document) => isManagedBySyncSource(document, input.sourceId));
7189
+ const staleDocuments = managedDocuments.filter((document) => !nextDocumentIds.has(document.id));
7190
+ const changedPrepared = prepared.filter((document) => {
7191
+ const existing = managedDocuments.find((entry) => entry.id === document.documentId);
7192
+ if (!existing) {
7193
+ return true;
7194
+ }
7195
+ return getDocumentSyncFingerprint(existing) !== nextFingerprintById.get(document.documentId);
7196
+ });
7197
+ if (input.deleteDocument) {
7198
+ await Promise.all(staleDocuments.map(async (document) => {
7199
+ await input.deleteDocument?.(document.id);
7200
+ }));
7201
+ }
7202
+ if (changedPrepared.length > 0) {
7203
+ await input.collection.ingest({
7204
+ chunks: changedPrepared.flatMap((document) => document.chunks)
7205
+ });
7206
+ }
7207
+ return {
7208
+ chunkCount: prepared.reduce((sum, document) => sum + document.chunks.length, 0),
7209
+ deletedCount: staleDocuments.length,
7210
+ documentCount: prepared.length,
7211
+ updatedCount: changedPrepared.length
7212
+ };
7213
+ };
7214
+ var toSourceRecord = (source, overrides) => ({
7215
+ description: source.description,
7216
+ id: source.id,
7217
+ kind: source.kind,
7218
+ label: source.label,
7219
+ metadata: source.metadata,
7220
+ status: "idle",
7221
+ target: source.target,
7222
+ ...overrides
7223
+ });
7224
+ var createRAGDirectorySyncSource = (options) => ({
7225
+ description: options.description,
7226
+ id: options.id,
7227
+ kind: "directory",
7228
+ label: options.label,
7229
+ metadata: options.metadata,
7230
+ target: options.directory,
7231
+ sync: async ({ collection, deleteDocument, listDocuments }) => {
7232
+ const loaded = await loadRAGDocumentsFromDirectory({
7233
+ baseMetadata: options.baseMetadata,
7234
+ defaultChunking: options.defaultChunking,
7235
+ directory: options.directory,
7236
+ extractors: options.extractors,
7237
+ includeExtensions: options.includeExtensions,
7238
+ recursive: options.recursive
7239
+ });
7240
+ const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.relativePath === "string" ? document.metadata.relativePath : document.source ?? document.title ?? ""));
7241
+ const reconciled = await reconcileManagedDocuments({
7242
+ collection,
7243
+ deleteDocument,
7244
+ documents: managedDocuments,
7245
+ listDocuments,
7246
+ sourceId: options.id
7247
+ });
7248
+ return {
7249
+ chunkCount: reconciled.chunkCount,
7250
+ documentCount: reconciled.documentCount,
7251
+ metadata: {
7252
+ deletedCount: reconciled.deletedCount,
7253
+ directory: options.directory,
7254
+ recursive: options.recursive !== false,
7255
+ updatedCount: reconciled.updatedCount
7256
+ }
7257
+ };
7258
+ }
7259
+ });
7260
+ var createRAGUrlSyncSource = (options) => ({
7261
+ description: options.description,
7262
+ id: options.id,
7263
+ kind: "url",
7264
+ label: options.label,
7265
+ metadata: options.metadata,
7266
+ target: options.urls.length === 1 ? options.urls[0]?.url : `${options.urls.length} urls`,
7267
+ sync: async ({ collection, deleteDocument, listDocuments }) => {
7268
+ const loaded = await loadRAGDocumentsFromURLs({
7269
+ baseMetadata: options.baseMetadata,
7270
+ defaultChunking: options.defaultChunking,
7271
+ extractors: options.extractors,
7272
+ urls: options.urls
7273
+ });
7274
+ const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.sourceUrl === "string" ? document.metadata.sourceUrl : document.source ?? document.title ?? ""));
7275
+ const reconciled = await reconcileManagedDocuments({
7276
+ collection,
7277
+ deleteDocument,
7278
+ documents: managedDocuments,
7279
+ listDocuments,
7280
+ sourceId: options.id
7281
+ });
7282
+ return {
7283
+ chunkCount: reconciled.chunkCount,
7284
+ documentCount: reconciled.documentCount,
7285
+ metadata: {
7286
+ deletedCount: reconciled.deletedCount,
7287
+ updatedCount: reconciled.updatedCount,
7288
+ urlCount: options.urls.length
7289
+ }
7290
+ };
7291
+ }
7292
+ });
7293
+ var createRAGSyncManager = (options) => {
7294
+ const sourceMap = new Map(options.sources.map((source) => [source.id, source]));
7295
+ const state = new Map(options.sources.map((source) => [source.id, toSourceRecord(source)]));
7296
+ const runSource = async (source) => {
7297
+ const running = toSourceRecord(source, {
7298
+ lastError: undefined,
7299
+ status: "running"
7300
+ });
7301
+ state.set(source.id, running);
7302
+ const startedAt = Date.now();
7303
+ try {
7304
+ const result = await source.sync({
7305
+ collection: options.collection,
7306
+ deleteDocument: options.deleteDocument,
7307
+ listDocuments: options.listDocuments
7308
+ });
7309
+ const completed = toSourceRecord(source, {
7310
+ chunkCount: result.chunkCount,
7311
+ documentCount: result.documentCount,
7312
+ lastError: undefined,
7313
+ lastSyncedAt: Date.now(),
7314
+ lastSyncDurationMs: Date.now() - startedAt,
7315
+ metadata: result.metadata === undefined ? source.metadata : {
7316
+ ...source.metadata ?? {},
7317
+ ...result.metadata
7318
+ },
7319
+ status: "completed"
7320
+ });
7321
+ state.set(source.id, completed);
7322
+ return completed;
7323
+ } catch (caught) {
7324
+ const failed = toSourceRecord(source, {
7325
+ lastError: toSyncError(caught),
7326
+ lastSyncedAt: Date.now(),
7327
+ lastSyncDurationMs: Date.now() - startedAt,
7328
+ status: "failed"
7329
+ });
7330
+ state.set(source.id, failed);
7331
+ throw caught;
7332
+ }
7333
+ };
7334
+ return {
7335
+ listSyncSources: () => [...state.values()],
7336
+ syncAllSources: async () => {
7337
+ const sources = await Promise.all(options.sources.map(async (source) => runSource(source)));
7338
+ return { ok: true, sources };
7339
+ },
7340
+ syncSource: async (id) => {
7341
+ const source = sourceMap.get(id);
7342
+ if (!source) {
7343
+ return {
7344
+ error: `RAG sync source ${id} is not configured`,
7345
+ ok: false
7346
+ };
7347
+ }
7348
+ return {
7349
+ ok: true,
7350
+ source: await runSource(source)
7351
+ };
7352
+ }
7353
+ };
7354
+ };
7023
7355
  // src/ai/rag/adapters/utils.ts
7024
7356
  var vectorDimensionDefault = 24;
7025
7357
  var createRAGVector = (text, dimensions = vectorDimensionDefault) => {
@@ -8760,6 +9092,37 @@ var createRAGClient = (options) => {
8760
9092
  }
8761
9093
  return parseJson(response);
8762
9094
  },
9095
+ async syncSources() {
9096
+ const response = await fetchImpl(`${basePath}/sync`);
9097
+ if (!response.ok) {
9098
+ throw new Error(await toErrorMessage3(response));
9099
+ }
9100
+ return parseJson(response);
9101
+ },
9102
+ async syncAllSources() {
9103
+ const response = await fetchImpl(`${basePath}/sync`, {
9104
+ method: "POST"
9105
+ });
9106
+ if (!response.ok) {
9107
+ return {
9108
+ error: await toErrorMessage3(response),
9109
+ ok: false
9110
+ };
9111
+ }
9112
+ return parseJson(response);
9113
+ },
9114
+ async syncSource(id) {
9115
+ const response = await fetchImpl(`${basePath}/sync/${encodeURIComponent(id)}`, {
9116
+ method: "POST"
9117
+ });
9118
+ if (!response.ok) {
9119
+ return {
9120
+ error: await toErrorMessage3(response),
9121
+ ok: false
9122
+ };
9123
+ }
9124
+ return parseJson(response);
9125
+ },
8763
9126
  async reindexDocument(id) {
8764
9127
  const response = await fetchImpl(`${basePath}/reindex/documents/${encodeURIComponent(id)}`, {
8765
9128
  method: "POST"
@@ -8902,7 +9265,9 @@ export {
8902
9265
  createTextFileExtractor,
8903
9266
  createSQLiteRAGStore,
8904
9267
  createRAGVector,
9268
+ createRAGUrlSyncSource,
8905
9269
  createAIStream as createRAGTransport,
9270
+ createRAGSyncManager,
8906
9271
  createRAGReranker,
8907
9272
  createRAGQueryTransform,
8908
9273
  createRAGPDFOCRExtractor,
@@ -8915,6 +9280,7 @@ export {
8915
9280
  createRAGFileExtractor,
8916
9281
  createRAGEvaluationSuite,
8917
9282
  createRAGEmbeddingProvider,
9283
+ createRAGDirectorySyncSource,
8918
9284
  createRAGCollection,
8919
9285
  createRAGClient,
8920
9286
  createRAGArchiveFileExtractor,
@@ -8955,5 +9321,5 @@ export {
8955
9321
  aiChat
8956
9322
  };
8957
9323
 
8958
- //# debugId=55FD05298CEAFBDB64756E2164756E21
9324
+ //# debugId=59C623D542A7B40F64756E2164756E21
8959
9325
  //# sourceMappingURL=index.js.map