Eloquent

Documentation

@elqnt/docs

Document management and processing for the Eloquent platform. Handle document libraries, folders, file uploads, content extraction, projects, and AI-powered analysis.

Installation

npm install @elqnt/docs

Entry Points

ImportDescription
@elqnt/docsAll exports
@elqnt/docs/hooksReact hooks (useLibraries, useFolders, useDocuments, useProjects)
@elqnt/docs/modelsTypeScript types
@elqnt/docs/apiInternal API functions (use hooks instead)
@elqnt/docs/transportAnalysis SSE transport (createAnalysisTransport, createFetchAnalysisTransport)

React Hooks

useLibraries

Hook for document library CRUD operations with loading/error states.

import { useLibraries } from "@elqnt/docs/hooks";

const {
  loading,
  error,
  listLibraries,
  getLibrary,
  getDefaultLibrary,
  createLibrary,
  updateLibrary,
  deleteLibrary,
} = useLibraries({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
});

// List all libraries
const libraries = await listLibraries();

// Get library by ID
const library = await getLibrary("library-uuid");

// Get or create default library for current user
const defaultLib = await getDefaultLibrary();

// Create library
const newLib = await createLibrary({
  name: "My Documents",
  description: "Personal files",
  permissionLevel: "user",
});

// Update library
const updated = await updateLibrary("library-uuid", { name: "Renamed Library" });

// Delete library
const success = await deleteLibrary("library-uuid");

Methods:

MethodParametersReturnsDescription
listLibraries-DocLibrary[]List all accessible libraries
getLibrarylibraryId: stringDocLibrary | nullGet library by ID
getDefaultLibrary-DocLibrary | nullGet or create user's default library
createLibrarylibrary: Partial<DocLibrary>DocLibrary | nullCreate a new library
updateLibrarylibraryId: string, updates: Partial<DocLibrary>DocLibrary | nullUpdate a library
deleteLibrarylibraryId: stringbooleanDelete a library

useFolders

Hook for folder CRUD operations with loading/error states.

import { useFolders } from "@elqnt/docs/hooks";

const {
  loading,
  error,
  listFolders,
  createFolder,
  updateFolder,
  deleteFolder,
  moveFolder,
  copyFolder,
} = useFolders({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
  libraryId: selectedLibraryId,
});

// List folders in root (no parentFolderId) or in a specific folder
const rootFolders = await listFolders();
const subFolders = await listFolders("parent-folder-uuid");

// Create folder
const newFolder = await createFolder({
  name: "Reports",
  libraryId: selectedLibraryId,
  parentFolderId: "", // Empty = root
});

// Update folder
const updated = await updateFolder("folder-uuid", { name: "Q4 Reports" });

// Delete folder (with optional recursive delete)
const success = await deleteFolder("folder-uuid", true); // recursive = true

// Move folder to new location
const moved = await moveFolder("folder-uuid", {
  newParentFolderId: "target-folder-uuid",
  newLibraryId: "target-library-uuid",
});

// Copy folder
const copied = await copyFolder("folder-uuid", {
  newParentFolderId: "target-folder-uuid",
});

Methods:

MethodParametersReturnsDescription
listFoldersparentFolderId?: stringDocFolder[]List folders (root or in parent)
createFolderfolder: Partial<DocFolder>DocFolder | nullCreate a new folder
updateFolderfolderId: string, updates: Partial<DocFolder>DocFolder | nullUpdate a folder
deleteFolderfolderId: string, recursive?: booleanbooleanDelete folder (optionally with contents)
moveFolderfolderId: string, destination: {...}DocFolder | nullMove folder to new location
copyFolderfolderId: string, destination: {...}DocFolder | nullCopy folder to new location

useDocuments

Hook for document CRUD operations with loading/error states.

import { useDocuments } from "@elqnt/docs/hooks";

const {
  loading,
  error,
  listDocuments,
  createDocument,
  updateDocument,
  deleteDocument,
  moveDocument,
  copyDocument,
} = useDocuments({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
  libraryId: selectedLibraryId,
});

// List documents with pagination
const { references, totalCount } = await listDocuments({
  folderId: "folder-uuid", // Optional: filter by folder
  page: 1,
  pageSize: 20,
});

// Create document reference
const newDoc = await createDocument({
  title: "Report.pdf",
  libraryId: selectedLibraryId,
  docUrl: "https://storage.example.com/file.pdf",
  mimeType: "application/pdf",
  fileSize: 1024000,
});

// Update document
const updated = await updateDocument("doc-uuid", { title: "Updated Report.pdf" });

// Delete document
const success = await deleteDocument("doc-uuid");

// Move document to new location
const moved = await moveDocument("doc-uuid", {
  newLibraryId: "target-library-uuid",
  newFolderId: "target-folder-uuid",
});

// Copy document
const copied = await copyDocument("doc-uuid", {
  newFolderId: "target-folder-uuid",
});

Methods:

MethodParametersReturnsDescription
listDocumentsparams?: ListDocumentsParams{ references, totalCount }List documents with pagination
createDocumentdocument: Partial<DocReference>DocReference | nullCreate a document reference
updateDocumentreferenceId: string, updates: Partial<DocReference>DocReference | nullUpdate a document
deleteDocumentreferenceId: stringbooleanDelete a document
moveDocumentreferenceId: string, destination: {...}DocReference | nullMove document to new location
copyDocumentreferenceId: string, destination: {...}DocReference | nullCopy document to new location

useProjects

Hook for Eloquent project CRUD operations with loading/error states.

import { useProjects } from "@elqnt/docs/hooks";

const {
  loading,
  error,
  listProjects,
  getProject,
  createProject,
  updateProject,
  deleteProject,
  persistProjectSources,
  saveDocumentToProject,
} = useProjects({
  baseUrl: apiGatewayUrl,
  orgId: selectedOrgId,
});

// List projects with pagination
const { projects, totalCount, totalPages } = await listProjects({
  page: 1,
  pageSize: 20,
});

// Get project by ID
const project = await getProject("project-uuid");

// Create project
const newProject = await createProject({
  name: "Q4 Analysis",
  description: "Quarterly analysis project",
  icon: "chart",
  color: "#3b82f6",
});

// Update project
const updated = await updateProject("project-uuid", {
  name: "Updated Project Name",
  description: "New description",
});

// Delete project
const success = await deleteProject("project-uuid");

// Persist project sources (replace all sources)
const sourcesUpdated = await persistProjectSources("project-uuid", [
  { id: "src-1", type: "document", referenceId: "doc-uuid", name: "Report.pdf", addedAt: new Date().toISOString() },
  { id: "src-2", type: "library", referenceId: "lib-uuid", name: "Team Docs", addedAt: new Date().toISOString() },
  { id: "src-3", type: "kg", referenceId: "kg-uuid", name: "Knowledge Base", addedAt: new Date().toISOString() },
]);

// Add single document to project sources
const docAdded = await saveDocumentToProject("project-uuid", "doc-uuid");

Methods:

MethodParametersReturnsDescription
listProjects{ page?, pageSize? }ProjectsListResultList projects with pagination
getProjectprojectId: stringEloquentProject | nullGet project by ID
createProjectproject: Partial<EloquentProject>EloquentProject | nullCreate a new project
updateProjectprojectId: string, updates: Partial<EloquentProject>EloquentProject | nullUpdate a project
deleteProjectprojectId: stringbooleanDelete a project
persistProjectSourcesprojectId: string, sources: ProjectSource[]booleanReplace all project sources
saveDocumentToProjectprojectId: string, documentId: stringbooleanAdd document to project sources

ProjectsListResult:

interface ProjectsListResult {
  projects: EloquentProject[];
  totalCount: number;
  currentPage: number;
  pageSize: number;
  totalPages: number;
}

Document Analysis API

Functions for AI-powered document analysis (not CRUD, so exposed as API functions).

import { getRawDocumentAnalysisApi } from "@elqnt/docs/api";

// Analyze document with AI
const analysis = await getRawDocumentAnalysisApi(
  {
    fileUrl: "https://storage.example.com/document.pdf",
    model: "claude-3-haiku-20240307",
    fromPage: 1,
    toPage: 10,
    outputMarkdown: true,
  },
  {
    baseUrl: config.apiGatewayUrl,
    orgId: config.orgId,
  }
);

Transport Layer

Platform-agnostic transport abstractions for document analysis SSE streaming. Supports both browser (EventSource) and React Native (fetch-based) environments.

createAnalysisTransport (Browser)

SSE transport using native EventSource for browser environments.

import { createAnalysisTransport } from "@elqnt/docs/transport";

const transport = createAnalysisTransport({ debug: true });
await transport.connect({ baseUrl: "https://api.example.com", orgId: "org-123" });

// Subscribe to events
transport.on("progress", (event) => {
  console.log(`Progress: ${event.percentage}% - ${event.message}`);
});

transport.on("completed", (event) => {
  console.log("Analysis complete:", event.result);
});

transport.on("error", (event) => {
  console.error("Analysis failed:", event.error);
});

// Start standard analysis
const jobId = await transport.startAnalysis({
  documentUrl: "https://storage.example.com/document.pdf",
  outputMarkdown: true,
});

// Or use smart analysis with AI processing
const smartJobId = await transport.startSmartAnalysis({
  documentUrl: "https://storage.example.com/document.pdf",
  title: "Financial Report Q4 2024",
  metadata: { fileSize: 1024000, mimeType: "application/pdf" },
});

// Clean up when done
transport.disconnect();

createFetchAnalysisTransport (React Native)

Fetch-based SSE transport for React Native and environments without native EventSource.

import { createFetchAnalysisTransport } from "@elqnt/docs/transport";

// Use fetch-based transport for React Native
const transport = createFetchAnalysisTransport({
  debug: true,
  customFetch: fetch, // Optional: pass custom fetch implementation
  analysisTimeout: 300000, // 5 minutes (default)
});

await transport.connect({ baseUrl, orgId });

transport.on("progress", (e) => console.log(`${e.percentage}%`));
transport.on("completed", (e) => console.log("Done:", e.result));

await transport.startSmartAnalysis({ documentUrl });

React Hook Integration

import { useState, useCallback } from "react";
import { createAnalysisTransport } from "@elqnt/docs/transport";

function useDocumentAnalysis(baseUrl: string, orgId: string) {
  const [progress, setProgress] = useState(0);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);
  const [analyzing, setAnalyzing] = useState(false);

  const analyze = useCallback(async (documentUrl: string) => {
    const transport = createAnalysisTransport();
    await transport.connect({ baseUrl, orgId });

    setAnalyzing(true);
    setProgress(0);
    setError(null);

    transport.on("progress", (e) => setProgress(e.percentage));
    transport.on("completed", (e) => {
      setResult(e.result);
      setAnalyzing(false);
      transport.disconnect();
    });
    transport.on("error", (e) => {
      setError(e.error);
      setAnalyzing(false);
      transport.disconnect();
    });

    await transport.startSmartAnalysis({ documentUrl });
  }, [baseUrl, orgId]);

  return { analyze, progress, result, error, analyzing };
}

Transport Interface

interface AnalysisTransport {
  connect(config: AnalysisTransportConfig): Promise<void>;
  disconnect(): void;
  startAnalysis(options: StartAnalysisOptions): Promise<string>;
  startSmartAnalysis(options: StartSmartAnalysisOptions): Promise<string>;
  onEvent(handler: EventHandler): Unsubscribe;
  on<T extends AnalysisEvent["type"]>(eventType: T, handler: EventHandler<T>): Unsubscribe;
  getState(): TransportState;
  getMetrics(): ConnectionMetrics;
  getError(): TransportError | undefined;
  clearError(): void;
}

Transport States:

StateDescription
disconnectedNot connected
connectingConnection in progress
connectedReady to start analysis
analyzingAnalysis in progress

Event Types:

EventDescription
progressProgress update with percentage and message
statusProcessing status change
startedAnalysis job started
completedAnalysis completed with result
errorAnalysis failed
page_processedSingle page processed (multi-page docs)
chunk_createdChunk created (embedding strategy)

Transport Types

import type {
  AnalysisTransport,
  AnalysisTransportConfig,
  TransportState,
  TransportError,
  ConnectionMetrics,
  AnalysisEvent,
  AnalysisProgressEvent,
  AnalysisCompletedEvent,
  AnalysisErrorEvent,
  StartAnalysisOptions,
  StartSmartAnalysisOptions,
} from "@elqnt/docs/transport";

interface AnalysisTransportConfig {
  baseUrl: string;
  orgId: string;
  userId?: string;
  debug?: boolean;
}

interface StartAnalysisOptions {
  documentUrl: string;
  model?: string;
  fromPage?: number;
  toPage?: number;
  outputMarkdown?: boolean;
}

interface StartSmartAnalysisOptions {
  documentUrl: string;
  title?: string;
  metadata?: {
    fileSize?: number;
    mimeType?: string;
    extension?: string;
  };
  strategy?: string;
}

interface AnalysisProgressEvent {
  type: "progress";
  timestamp: number;
  jobId?: string;
  percentage: number;
  message: string;
  currentStep?: string;
  totalSteps?: number;
}

interface AnalysisCompletedEvent {
  type: "completed";
  timestamp: number;
  jobId: string;
  result: DocumentAnalysisResult | ProcessingResult;
}

interface TransportError {
  code: "CONNECTION_FAILED" | "PARSE_ERROR" | "ANALYSIS_FAILED" | "TIMEOUT" | "NETWORK_ERROR" | "AUTH_FAILED";
  message: string;
  retryable: boolean;
  timestamp: number;
}

Models

DocLibrary

import type { DocLibrary } from "@elqnt/docs/models";

interface DocLibrary {
  id?: string;
  name: string;
  description: string;
  icon: string;
  color: string;
  ownerEmail: string;
  permissionLevel: string; // "user" | "team" | "public"
  teamMembers: string[];
  isDefault: boolean;
  docCount: number;
  folderCount: number;
  totalSizeBytes: number;
  createdAt: string;
  updatedAt: string;
}

DocFolder

import type { DocFolder, FolderTreeNode } from "@elqnt/docs/models";

interface DocFolder {
  id?: string;
  libraryId: string;
  parentFolderId: string; // Empty string = root
  name: string;
  path: string; // Full path: /folder1/folder2/
  depth: number;
  icon: string;
  color: string;
  inheritPermissions: boolean;
  permissionLevel: string; // Only if !inheritPermissions
  teamMembers: string[]; // Only if !inheritPermissions
  ownerEmail: string;
  docCount: number;
  subfolderCount: number;
  createdAt: string;
  updatedAt: string;
}

interface FolderTreeNode {
  folder: DocFolder;
  children: FolderTreeNode[];
  documents: DocReference[];
}

DocReference

import type { DocReference } from "@elqnt/docs/models";

interface DocReference {
  id?: string;
  docId: string; // References doc_metadata in ClickHouse
  libraryId: string;
  folderId: string; // Empty = library root
  title: string;
  docUrl: string;
  fileSize: number;
  mimeType: string;
  ownerEmail: string;
  status: string; // "processing" | "completed" | "failed"
  createdAt: string;
  updatedAt: string;
}

Project Types

import type {
  EloquentProject,
  ProjectSource,
  ProjectChatSummary,
} from "@elqnt/docs/models";

interface EloquentProject {
  id?: string;
  name: string;
  description: string;
  icon: string;
  color: string;
  ownerEmail: string;
  permissionLevel: string;
  teamMembers: string[];
  systemPrompt: string;
  chatKeys: string[];
  chats: ProjectChatSummary[];
  libraryId: string;
  linkedLibraryIds: string[];
  sources: ProjectSource[];
  chatCount: number;
  sourceCount: number;
  fileCount: number;
  createdAt: string;
  updatedAt: string;
}

interface ProjectSource {
  id: string;
  type: string; // "document" | "library" | "folder" | "kg" | "upload"
  referenceId: string;
  name: string;
  addedAt: string;
}

interface ProjectChatSummary {
  chatKey: string;
  title: string;
  agentName?: string;
  agentId?: string;
  createdAt: number;
}

Document Analysis Types

import type {
  DocumentAnalysisProgressEvent,
  DocumentAnalysisProgress,
  DocumentAnalysisResultSSE,
  SmartAnalysisRequest,
  SmartAnalysisProgressEvent,
} from "@elqnt/docs/models";

interface DocumentAnalysisProgressEvent {
  jobId: string;
  type: string; // "started" | "extracting" | "extracted" | "chunking" | "chunked" | "embedding" | "storing" | "completed" | "error"
  message: string;
  progress?: DocumentAnalysisProgress;
  result?: DocumentAnalysisResultSSE;
  timestamp: string;
}

interface DocumentAnalysisProgress {
  stage: string; // "extraction" | "chunking" | "embedding" | "storing"
  currentStep: number;
  totalSteps: number;
  pageCount?: number;
  chunksCreated?: number;
  chunksEmbedded?: number;
  percentComplete: number;
}

interface SmartAnalysisRequest {
  url: string;
  fileName: string;
  metadata?: AttachmentFileMetadata;
  chatKey?: string;
  messageId?: string;
  orgId?: string;
  userEmail?: string;
}

Permission Levels

import {
  PermissionUser,
  PermissionTeam,
  PermissionPublic,
} from "@elqnt/docs/models";

PermissionUser;   // "user" - Only owner
PermissionTeam;   // "team" - Owner + team members
PermissionPublic; // "public" - All org members

Progress Event Types

import {
  ProgressTypeStarted,
  ProgressTypeExtracting,
  ProgressTypeExtracted,
  ProgressTypeChunking,
  ProgressTypeChunked,
  ProgressTypeEmbedding,
  ProgressTypeStoring,
  ProgressTypeCompleted,
  ProgressTypeError,
  StageExtraction,
  StageChunking,
  StageEmbedding,
  StageStoring,
} from "@elqnt/docs/models";

NATS Subjects

For backend service communication:

import {
  // Library subjects
  CreateLibrarySubject,
  GetLibrarySubject,
  UpdateLibrarySubject,
  DeleteLibrarySubject,
  QueryLibrariesSubject,
  GetOrCreateDefaultLibrarySubject,
  // Folder subjects
  CreateFolderSubject,
  GetFolderSubject,
  UpdateFolderSubject,
  DeleteFolderSubject,
  QueryFoldersSubject,
  GetFolderTreeSubject,
  MoveFolderSubject,
  CopyFolderSubject,
  // Document subjects
  CreateDocReferenceSubject,
  UpdateDocReferenceSubject,
  CopyDocReferenceSubject,
  MoveDocumentSubject,
  MoveDocumentsBulkSubject,
  QueryDocReferencesSubject,
  DeleteDocReferenceSubject,
  TrashDocReferenceSubject,
  RestoreDocReferenceSubject,
  // Project subjects
  CreateProjectSubject,
  GetProjectSubject,
  UpdateProjectSubject,
  DeleteProjectSubject,
  QueryProjectsSubject,
} from "@elqnt/docs/models";

API Gateway Routes

FunctionMethodGateway Route
Libraries
ListGET/api/v1/libraries
Get DefaultGET/api/v1/libraries/default
GetGET/api/v1/libraries/{libraryId}
CreatePOST/api/v1/libraries
UpdatePUT/api/v1/libraries/{libraryId}
DeleteDELETE/api/v1/libraries/{libraryId}
Folders
ListGET/api/v1/libraries/{libraryId}/folders
CreatePOST/api/v1/folders
UpdatePUT/api/v1/folders/{folderId}
DeleteDELETE/api/v1/folders/{folderId}
MovePOST/api/v1/folders/{folderId}/move
CopyPOST/api/v1/folders/{folderId}/copy
Documents
ListGET/api/v1/libraries/{libraryId}/documents
CreatePOST/api/v1/documents
UpdatePUT/api/v1/documents/{referenceId}
DeleteDELETE/api/v1/documents/{referenceId}
MovePOST/api/v1/documents/{referenceId}/move
CopyPOST/api/v1/documents/{referenceId}/copy
Projects
ListGET/api/v1/projects
GetGET/api/v1/projects/{projectId}
CreatePOST/api/v1/projects
UpdatePUT/api/v1/projects/{projectId}
DeleteDELETE/api/v1/projects/{projectId}
Persist SourcesPUT/api/v1/projects/{projectId}/sources
Add DocumentPOST/api/v1/projects/{projectId}/sources/document
Analysis
AnalyzePOST/api/v1/documents/analyze
Start AnalysisPOST/api/v1/documents/analyze/start
Analysis StreamGET/api/v1/documents/analyze/stream
Start Smart AnalysisPOST/api/v1/documents/analyze/smart
Smart Analysis StreamGET/api/v1/documents/analyze/smart/stream

Type Generation

Document types are generated from Go using tygo:

cd backend && tygo generate
Go PackageTypeScript Output
blazi/common/docs@elqnt/docs/models/docs.ts
blazi/services/doc-libraries/models@elqnt/docs/models/document-libraries.ts

See Also