@absolutejs/absolute 0.19.0-beta.444 → 0.19.0-beta.446

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
@@ -2467,6 +2467,85 @@ var createRAGCollection = (options) => {
2467
2467
  };
2468
2468
  };
2469
2469
 
2470
+ // src/ai/rag/htmxWorkflowRenderers.ts
2471
+ var escapeHtml2 = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2472
+ var renderEmptyState = (kind) => {
2473
+ switch (kind) {
2474
+ case "documents":
2475
+ return '<p class="rag-empty">No documents indexed.</p>';
2476
+ case "searchResults":
2477
+ return '<p class="rag-empty">No matching chunks.</p>';
2478
+ case "chunkPreview":
2479
+ return '<p class="rag-empty">No chunk preview available.</p>';
2480
+ case "status":
2481
+ default:
2482
+ return '<p class="rag-empty">No status available.</p>';
2483
+ }
2484
+ };
2485
+ var renderCapabilityList = (capabilities) => {
2486
+ if (!capabilities) {
2487
+ return "";
2488
+ }
2489
+ const items = [
2490
+ `backend=${capabilities.backend}`,
2491
+ `persistence=${capabilities.persistence}`,
2492
+ `nativeVectorSearch=${capabilities.nativeVectorSearch ? "true" : "false"}`,
2493
+ `serverSideFiltering=${capabilities.serverSideFiltering ? "true" : "false"}`,
2494
+ `streamingIngestStatus=${capabilities.streamingIngestStatus ? "true" : "false"}`
2495
+ ];
2496
+ return '<ul class="rag-status-capabilities">' + items.map((item) => `<li>${escapeHtml2(item)}</li>`).join("") + "</ul>";
2497
+ };
2498
+ var defaultStatus = ({
2499
+ status,
2500
+ capabilities
2501
+ }) => {
2502
+ if (!status) {
2503
+ return renderEmptyState("status");
2504
+ }
2505
+ return '<dl class="rag-status">' + `<div><dt>Backend</dt><dd>${escapeHtml2(status.backend)}</dd></div>` + `<div><dt>Vector mode</dt><dd>${escapeHtml2(status.vectorMode)}</dd></div>` + `<div><dt>Embedding dimensions</dt><dd>${status.dimensions ?? "n/a"}</dd></div>` + `<div><dt>Vector acceleration</dt><dd>${status.native?.active ? "active" : "inactive"}</dd></div>` + "</dl>" + renderCapabilityList(capabilities);
2506
+ };
2507
+ var defaultSearchResultItem = (source, index) => '<article class="rag-search-result">' + `<h3>${escapeHtml2(source.title ?? source.chunkId ?? `Result ${index + 1}`)}</h3>` + `<p class="rag-search-source">${escapeHtml2(source.source ?? "unknown source")}</p>` + `<p class="rag-search-score">score ${source.score.toFixed(3)}</p>` + `<p class="rag-search-text">${escapeHtml2(source.text)}</p>` + "</article>";
2508
+ var defaultSearchResults = ({
2509
+ query,
2510
+ results
2511
+ }) => results.length === 0 ? renderEmptyState("searchResults") : '<section class="rag-search-results">' + `<p class="rag-search-summary">${results.length} results for ${escapeHtml2(query)}</p>` + results.map((result, index) => defaultSearchResultItem(result, index)).join("") + "</section>";
2512
+ var defaultDocumentItem = (document, index) => '<article class="rag-document">' + `<h3>${escapeHtml2(document.title || `Document ${index + 1}`)}</h3>` + `<p class="rag-document-id">${escapeHtml2(document.id)}</p>` + `<p class="rag-document-source">${escapeHtml2(document.source)}</p>` + `<p class="rag-document-meta">${escapeHtml2(document.format ?? "text")} \xB7 ${escapeHtml2(document.chunkStrategy ?? "paragraphs")} \xB7 ${document.chunkCount ?? 0} chunks</p>` + "</article>";
2513
+ var defaultDocuments = ({
2514
+ documents
2515
+ }) => documents.length === 0 ? renderEmptyState("documents") : '<section class="rag-documents">' + documents.map((document, index) => defaultDocumentItem(document, index)).join("") + "</section>";
2516
+ var defaultChunkPreview = (input) => '<section class="rag-chunk-preview">' + `<h3>${escapeHtml2(input.document.title)}</h3>` + `<p class="rag-chunk-preview-source">${escapeHtml2(input.document.source)}</p>` + '<article class="rag-chunk-normalized">' + "<h4>Normalized text</h4>" + `<pre>${escapeHtml2(input.normalizedText)}</pre>` + "</article>" + input.chunks.map((chunk) => '<article class="rag-chunk">' + `<h4>${escapeHtml2(chunk.chunkId)}</h4>` + `<p class="rag-chunk-meta">chunk ${chunk.metadata?.chunkIndex ?? 0} of ${chunk.metadata?.chunkCount ?? input.chunks.length}</p>` + `<pre>${escapeHtml2(chunk.text)}</pre>` + "</article>").join("") + "</section>";
2517
+ var defaultMutationResult = (input) => {
2518
+ if (!input.ok) {
2519
+ return `<div class="rag-mutation error">${escapeHtml2(input.error ?? "Request failed")}</div>`;
2520
+ }
2521
+ const details = [];
2522
+ if (input.status) {
2523
+ details.push(input.status);
2524
+ }
2525
+ if (input.inserted) {
2526
+ details.push(`inserted=${input.inserted}`);
2527
+ }
2528
+ if (input.deleted) {
2529
+ details.push(`deleted=${input.deleted}`);
2530
+ }
2531
+ if (typeof input.documents === "number") {
2532
+ details.push(`documents=${input.documents}`);
2533
+ }
2534
+ return `<div class="rag-mutation ok">${escapeHtml2(details.join(" \xB7 ") || "ok")}</div>`;
2535
+ };
2536
+ var defaultError2 = (message) => `<div class="rag-error">${escapeHtml2(message)}</div>`;
2537
+ var resolveRAGWorkflowRenderers = (custom) => ({
2538
+ status: custom?.status ?? defaultStatus,
2539
+ searchResults: custom?.searchResults ?? defaultSearchResults,
2540
+ searchResultItem: custom?.searchResultItem ?? defaultSearchResultItem,
2541
+ documents: custom?.documents ?? defaultDocuments,
2542
+ documentItem: custom?.documentItem ?? defaultDocumentItem,
2543
+ chunkPreview: custom?.chunkPreview ?? defaultChunkPreview,
2544
+ mutationResult: custom?.mutationResult ?? defaultMutationResult,
2545
+ emptyState: custom?.emptyState ?? renderEmptyState,
2546
+ error: custom?.error ?? defaultError2
2547
+ });
2548
+
2470
2549
  // src/ai/rag/types.ts
2471
2550
  var buildRAGContext = (hits) => {
2472
2551
  if (hits.length === 0) {
@@ -2492,6 +2571,7 @@ var DEFAULT_TOP_K2 = 6;
2492
2571
  var DEFAULT_PREFIX_LEN = 12;
2493
2572
  var DEFAULT_PROVIDER = "anthropic";
2494
2573
  var TITLE_MAX_LENGTH2 = 80;
2574
+ var HTML_HEADERS = { "Content-Type": "text/html; charset=utf-8" };
2495
2575
  var defaultParseProvider2 = (content) => {
2496
2576
  const colonIdx = content.indexOf(":");
2497
2577
  const hasPrefix = colonIdx > 0 && colonIdx < DEFAULT_PREFIX_LEN;
@@ -2502,6 +2582,7 @@ var defaultParseProvider2 = (content) => {
2502
2582
  };
2503
2583
  };
2504
2584
  var normalizeScore = (value) => Number.isFinite(value) ? value : 0;
2585
+ var isHTMXRequest = (request) => request.headers.get("HX-Request") === "true";
2505
2586
  var buildSources = (results) => results.map((result) => ({
2506
2587
  chunkId: result.chunkId,
2507
2588
  score: normalizeScore(result.score),
@@ -2619,6 +2700,14 @@ var ragChat = (config) => {
2619
2700
  const abortControllers = new Map;
2620
2701
  const includeCompleteSources = config.ragCompleteSources === true;
2621
2702
  const indexManager = config.indexManager;
2703
+ const workflowRenderers = resolveRAGWorkflowRenderers(typeof config.htmx === "object" ? config.htmx.workflow?.render : undefined);
2704
+ const toHTMXResponse = (html, status, extraHeaders) => new Response(html, {
2705
+ headers: {
2706
+ ...HTML_HEADERS,
2707
+ ...extraHeaders
2708
+ },
2709
+ status: typeof status === "number" ? status : 200
2710
+ });
2622
2711
  const appendMessage2 = (conversation, message) => {
2623
2712
  conversation.messages.push(message);
2624
2713
  conversation.lastMessageAt = Date.now();
@@ -3008,23 +3097,68 @@ var ragChat = (config) => {
3008
3097
  await handleMessage(ws, msg.content, msg.conversationId, msg.attachments);
3009
3098
  }
3010
3099
  }
3011
- }).post(`${path}/search`, async ({ body }) => handleSearch(body)).get(`${path}/status`, () => handleStatus()).get(`${path}/documents`, async ({ query, set }) => {
3100
+ }).post(`${path}/search`, async ({ body, request, set }) => {
3101
+ const result = await handleSearch(body);
3102
+ if (!result.ok) {
3103
+ set.status = result.error === "Invalid payload" || result.error?.startsWith("Expected payload shape:") ? 400 : 404;
3104
+ }
3105
+ if (config.htmx && isHTMXRequest(request)) {
3106
+ if (!result.ok) {
3107
+ return toHTMXResponse(workflowRenderers.error(result.error ?? "Search failed"), set.status);
3108
+ }
3109
+ const query = body && typeof body === "object" && typeof body.query === "string" ? body.query : "";
3110
+ return toHTMXResponse(workflowRenderers.searchResults({
3111
+ query,
3112
+ results: result.results ?? []
3113
+ }));
3114
+ }
3115
+ return result;
3116
+ }).get(`${path}/status`, ({ request }) => {
3117
+ const result = handleStatus();
3118
+ if (config.htmx && isHTMXRequest(request)) {
3119
+ return toHTMXResponse(workflowRenderers.status({
3120
+ capabilities: result.capabilities,
3121
+ status: result.status
3122
+ }));
3123
+ }
3124
+ return result;
3125
+ }).get(`${path}/documents`, async ({ query, request, set }) => {
3012
3126
  const result = await handleDocuments(typeof query?.kind === "string" ? query.kind : undefined);
3013
3127
  if (!result.ok) {
3014
3128
  set.status = 404;
3015
3129
  }
3130
+ if (config.htmx && isHTMXRequest(request)) {
3131
+ if (!result.ok) {
3132
+ return toHTMXResponse(workflowRenderers.error(result.error), set.status);
3133
+ }
3134
+ return toHTMXResponse(workflowRenderers.documents({
3135
+ documents: result.documents
3136
+ }));
3137
+ }
3016
3138
  return result;
3017
- }).post(`${path}/documents`, async ({ body, set }) => {
3139
+ }).post(`${path}/documents`, async ({ body, request, set }) => {
3018
3140
  const result = await handleCreateDocument(body);
3019
3141
  if (!result.ok) {
3020
3142
  set.status = result.error === "Invalid payload" ? 400 : result.error?.includes("not configured") ? 404 : 400;
3021
3143
  }
3144
+ if (config.htmx && isHTMXRequest(request)) {
3145
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to create document");
3146
+ return toHTMXResponse(html, set.status, {
3147
+ "HX-Trigger": "rag:mutated"
3148
+ });
3149
+ }
3022
3150
  return result;
3023
- }).get(`${path}/documents/:id/chunks`, async ({ params, set }) => {
3151
+ }).get(`${path}/documents/:id/chunks`, async ({ params, request, set }) => {
3024
3152
  const result = await handleDocumentChunks(typeof params.id === "string" ? params.id.trim() : "");
3025
3153
  if (!result.ok) {
3026
3154
  set.status = result.error === "document id is required" ? 400 : result.error === "document not found" ? 404 : 404;
3027
3155
  }
3156
+ if (config.htmx && isHTMXRequest(request)) {
3157
+ if (!result.ok) {
3158
+ return toHTMXResponse(workflowRenderers.error(result.error), set.status);
3159
+ }
3160
+ return toHTMXResponse(workflowRenderers.chunkPreview(result));
3161
+ }
3028
3162
  return result;
3029
3163
  }).get(`${path}/backends`, async ({ set }) => {
3030
3164
  const result = await handleBackends();
@@ -3038,23 +3172,41 @@ var ragChat = (config) => {
3038
3172
  }
3039
3173
  await ragStore.clear?.();
3040
3174
  return { ok: true };
3041
- }).delete(`${path}/documents/:id`, async ({ params, set }) => {
3175
+ }).delete(`${path}/documents/:id`, async ({ params, request, set }) => {
3042
3176
  const result = await handleDeleteDocument(typeof params.id === "string" ? params.id.trim() : "");
3043
3177
  if (!result.ok) {
3044
3178
  set.status = result.error === "document id is required" ? 400 : result.error === "document not found" ? 404 : 404;
3045
3179
  }
3180
+ if (config.htmx && isHTMXRequest(request)) {
3181
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to delete document");
3182
+ return toHTMXResponse(html, set.status, {
3183
+ "HX-Trigger": "rag:mutated"
3184
+ });
3185
+ }
3046
3186
  return result;
3047
- }).post(`${path}/reseed`, async ({ set }) => {
3187
+ }).post(`${path}/reseed`, async ({ request, set }) => {
3048
3188
  const result = await handleReseed();
3049
3189
  if (!result.ok) {
3050
3190
  set.status = 404;
3051
3191
  }
3192
+ if (config.htmx && isHTMXRequest(request)) {
3193
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reseed index");
3194
+ return toHTMXResponse(html, set.status, {
3195
+ "HX-Trigger": "rag:mutated"
3196
+ });
3197
+ }
3052
3198
  return result;
3053
- }).post(`${path}/reset`, async ({ set }) => {
3199
+ }).post(`${path}/reset`, async ({ request, set }) => {
3054
3200
  const result = await handleReset();
3055
3201
  if (!result.ok) {
3056
3202
  set.status = 404;
3057
3203
  }
3204
+ if (config.htmx && isHTMXRequest(request)) {
3205
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reset index");
3206
+ return toHTMXResponse(html, set.status, {
3207
+ "HX-Trigger": "rag:mutated"
3208
+ });
3209
+ }
3058
3210
  return result;
3059
3211
  }).get(`${path}/conversations`, () => store.list()).get(`${path}/conversations/:id`, async ({ params }) => {
3060
3212
  const conv = await store.get(params.id);
@@ -4166,5 +4318,5 @@ export {
4166
4318
  aiChat
4167
4319
  };
4168
4320
 
4169
- //# debugId=C4FB024A221A442464756E2164756E21
4321
+ //# debugId=8FF738CEBD89B6A864756E2164756E21
4170
4322
  //# sourceMappingURL=index.js.map