@absolutejs/absolute 0.19.0-beta.443 → 0.19.0-beta.445

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,11 @@ 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) => new Response(html, {
2705
+ headers: HTML_HEADERS,
2706
+ status: typeof status === "number" ? status : 200
2707
+ });
2622
2708
  const appendMessage2 = (conversation, message) => {
2623
2709
  conversation.messages.push(message);
2624
2710
  conversation.lastMessageAt = Date.now();
@@ -3008,23 +3094,66 @@ var ragChat = (config) => {
3008
3094
  await handleMessage(ws, msg.content, msg.conversationId, msg.attachments);
3009
3095
  }
3010
3096
  }
3011
- }).post(`${path}/search`, async ({ body }) => handleSearch(body)).get(`${path}/status`, () => handleStatus()).get(`${path}/documents`, async ({ query, set }) => {
3097
+ }).post(`${path}/search`, async ({ body, request, set }) => {
3098
+ const result = await handleSearch(body);
3099
+ if (!result.ok) {
3100
+ set.status = result.error === "Invalid payload" || result.error?.startsWith("Expected payload shape:") ? 400 : 404;
3101
+ }
3102
+ if (config.htmx && isHTMXRequest(request)) {
3103
+ if (!result.ok) {
3104
+ return toHTMXResponse(workflowRenderers.error(result.error ?? "Search failed"), set.status);
3105
+ }
3106
+ const query = body && typeof body === "object" && typeof body.query === "string" ? body.query : "";
3107
+ return toHTMXResponse(workflowRenderers.searchResults({
3108
+ query,
3109
+ results: result.results ?? []
3110
+ }));
3111
+ }
3112
+ return result;
3113
+ }).get(`${path}/status`, ({ request }) => {
3114
+ const result = handleStatus();
3115
+ if (config.htmx && isHTMXRequest(request)) {
3116
+ return toHTMXResponse(workflowRenderers.status({
3117
+ capabilities: result.capabilities,
3118
+ status: result.status
3119
+ }));
3120
+ }
3121
+ return result;
3122
+ }).get(`${path}/documents`, async ({ query, request, set }) => {
3012
3123
  const result = await handleDocuments(typeof query?.kind === "string" ? query.kind : undefined);
3013
3124
  if (!result.ok) {
3014
3125
  set.status = 404;
3015
3126
  }
3127
+ if (config.htmx && isHTMXRequest(request)) {
3128
+ if (!result.ok) {
3129
+ return toHTMXResponse(workflowRenderers.error(result.error), set.status);
3130
+ }
3131
+ return toHTMXResponse(workflowRenderers.documents({
3132
+ documents: result.documents
3133
+ }));
3134
+ }
3016
3135
  return result;
3017
- }).post(`${path}/documents`, async ({ body, set }) => {
3136
+ }).post(`${path}/documents`, async ({ body, request, set }) => {
3018
3137
  const result = await handleCreateDocument(body);
3019
3138
  if (!result.ok) {
3020
3139
  set.status = result.error === "Invalid payload" ? 400 : result.error?.includes("not configured") ? 404 : 400;
3021
3140
  }
3141
+ if (config.htmx && isHTMXRequest(request)) {
3142
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to create document");
3143
+ return toHTMXResponse(html, set.status);
3144
+ }
3022
3145
  return result;
3023
- }).get(`${path}/documents/:id/chunks`, async ({ params, set }) => {
3146
+ }).get(`${path}/documents/:id/chunks`, async ({ params, request, set }) => {
3024
3147
  const result = await handleDocumentChunks(typeof params.id === "string" ? params.id.trim() : "");
3025
3148
  if (!result.ok) {
3026
3149
  set.status = result.error === "document id is required" ? 400 : result.error === "document not found" ? 404 : 404;
3027
3150
  }
3151
+ if (config.htmx && isHTMXRequest(request)) {
3152
+ if (!result.ok) {
3153
+ return toHTMXResponse(workflowRenderers.error(result.error), set.status);
3154
+ }
3155
+ return toHTMXResponse(workflowRenderers.chunkPreview(result));
3156
+ }
3028
3157
  return result;
3029
3158
  }).get(`${path}/backends`, async ({ set }) => {
3030
3159
  const result = await handleBackends();
@@ -3038,23 +3167,35 @@ var ragChat = (config) => {
3038
3167
  }
3039
3168
  await ragStore.clear?.();
3040
3169
  return { ok: true };
3041
- }).delete(`${path}/documents/:id`, async ({ params, set }) => {
3170
+ }).delete(`${path}/documents/:id`, async ({ params, request, set }) => {
3042
3171
  const result = await handleDeleteDocument(typeof params.id === "string" ? params.id.trim() : "");
3043
3172
  if (!result.ok) {
3044
3173
  set.status = result.error === "document id is required" ? 400 : result.error === "document not found" ? 404 : 404;
3045
3174
  }
3175
+ if (config.htmx && isHTMXRequest(request)) {
3176
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to delete document");
3177
+ return toHTMXResponse(html, set.status);
3178
+ }
3046
3179
  return result;
3047
- }).post(`${path}/reseed`, async ({ set }) => {
3180
+ }).post(`${path}/reseed`, async ({ request, set }) => {
3048
3181
  const result = await handleReseed();
3049
3182
  if (!result.ok) {
3050
3183
  set.status = 404;
3051
3184
  }
3185
+ if (config.htmx && isHTMXRequest(request)) {
3186
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reseed index");
3187
+ return toHTMXResponse(html, set.status);
3188
+ }
3052
3189
  return result;
3053
- }).post(`${path}/reset`, async ({ set }) => {
3190
+ }).post(`${path}/reset`, async ({ request, set }) => {
3054
3191
  const result = await handleReset();
3055
3192
  if (!result.ok) {
3056
3193
  set.status = 404;
3057
3194
  }
3195
+ if (config.htmx && isHTMXRequest(request)) {
3196
+ const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reset index");
3197
+ return toHTMXResponse(html, set.status);
3198
+ }
3058
3199
  return result;
3059
3200
  }).get(`${path}/conversations`, () => store.list()).get(`${path}/conversations/:id`, async ({ params }) => {
3060
3201
  const conv = await store.get(params.id);
@@ -4166,5 +4307,5 @@ export {
4166
4307
  aiChat
4167
4308
  };
4168
4309
 
4169
- //# debugId=C4FB024A221A442464756E2164756E21
4310
+ //# debugId=1CB9D35FC2D88FAA64756E2164756E21
4170
4311
  //# sourceMappingURL=index.js.map