@apart-tech/intelligence-core 1.12.1 → 1.13.0

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.
@@ -0,0 +1,273 @@
1
+ import { createHash } from "node:crypto";
2
+ import { SINGLE_TENANT_ORG_ID } from "../db/tenant.js";
3
+ const SIGNED_URL_EXPIRY_MS = 15 * 60 * 1000; // 15 minutes
4
+ const MIME_TO_EXT = {
5
+ "application/pdf": "pdf",
6
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
7
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
8
+ "image/png": "png",
9
+ "image/jpeg": "jpeg",
10
+ "image/avif": "avif",
11
+ };
12
+ export class DocumentService {
13
+ db;
14
+ storage;
15
+ bucketName;
16
+ ocrService;
17
+ nodeService;
18
+ chunkService;
19
+ tagService;
20
+ piiEncryption;
21
+ piiOptions;
22
+ tenantCtx;
23
+ constructor(db, storage, bucketName, ocrService, nodeService, chunkService, tagService, piiEncryption, piiOptions, tenantCtx) {
24
+ this.db = db;
25
+ this.storage = storage;
26
+ this.bucketName = bucketName;
27
+ this.ocrService = ocrService;
28
+ this.nodeService = nodeService;
29
+ this.chunkService = chunkService;
30
+ this.tagService = tagService;
31
+ this.piiEncryption = piiEncryption;
32
+ this.piiOptions = piiOptions;
33
+ this.tenantCtx = tenantCtx ?? { organizationId: SINGLE_TENANT_ORG_ID };
34
+ }
35
+ /**
36
+ * Create Document records and generate signed PUT URLs for direct upload.
37
+ */
38
+ async createUploadUrls(files, createdBy) {
39
+ const results = [];
40
+ for (const file of files) {
41
+ const ext = MIME_TO_EXT[file.mimeType] ?? "bin";
42
+ // Create Document record first to get the ID
43
+ const doc = await this.db.document.create({
44
+ data: {
45
+ fileName: file.fileName,
46
+ mimeType: file.mimeType,
47
+ fileSizeBytes: file.sizeBytes,
48
+ storagePath: "", // will be set below
49
+ status: "uploading",
50
+ createdBy,
51
+ organizationId: this.tenantCtx.organizationId,
52
+ },
53
+ });
54
+ const storagePath = `${this.tenantCtx.organizationId}/${doc.id}/original.${ext}`;
55
+ // Update with the real storage path
56
+ await this.db.document.update({
57
+ where: { id: doc.id },
58
+ data: { storagePath },
59
+ });
60
+ const bucket = this.storage.bucket(this.bucketName);
61
+ const gcsFile = bucket.file(storagePath);
62
+ const [uploadUrl] = await gcsFile.getSignedUrl({
63
+ version: "v4",
64
+ action: "write",
65
+ expires: Date.now() + SIGNED_URL_EXPIRY_MS,
66
+ contentType: file.mimeType,
67
+ });
68
+ results.push({
69
+ id: doc.id,
70
+ uploadUrl,
71
+ fileName: file.fileName,
72
+ });
73
+ }
74
+ return results;
75
+ }
76
+ /**
77
+ * Kick off async processing for uploaded documents.
78
+ * Returns immediately with current statuses; processing happens in background.
79
+ */
80
+ async processDocuments(documentIds) {
81
+ const warnings = [];
82
+ const statuses = [];
83
+ for (const id of documentIds) {
84
+ const doc = await this.db.document.findFirst({
85
+ where: { id },
86
+ });
87
+ if (!doc) {
88
+ warnings.push(`Document ${id} not found`);
89
+ continue;
90
+ }
91
+ if (doc.status !== "uploading") {
92
+ warnings.push(`Document ${id} is in status "${doc.status}", expected "uploading"`);
93
+ statuses.push({ id: doc.id, status: doc.status });
94
+ continue;
95
+ }
96
+ // Set status to processing
97
+ await this.db.document.update({
98
+ where: { id: doc.id },
99
+ data: { status: "processing" },
100
+ });
101
+ statuses.push({ id: doc.id, status: "processing" });
102
+ // Fire-and-forget: process in background
103
+ this.processDocument(doc.id).catch((err) => {
104
+ console.error(`Document processing failed for ${doc.id}:`, err);
105
+ });
106
+ }
107
+ return { documents: statuses, warnings };
108
+ }
109
+ /**
110
+ * Process a single document: verify GCS → hash → dedup → OCR → create node → PII → chunk → tag.
111
+ */
112
+ async processDocument(documentId) {
113
+ const doc = await this.db.document.findFirst({
114
+ where: { id: documentId },
115
+ });
116
+ if (!doc) {
117
+ throw new Error(`Document ${documentId} not found`);
118
+ }
119
+ try {
120
+ // 1. Verify file exists in GCS
121
+ const bucket = this.storage.bucket(this.bucketName);
122
+ const gcsFile = bucket.file(doc.storagePath);
123
+ const [exists] = await gcsFile.exists();
124
+ if (!exists) {
125
+ throw new Error(`File not found in GCS: ${doc.storagePath}`);
126
+ }
127
+ // 2. Compute SHA-256 content hash
128
+ const [fileBuffer] = await gcsFile.download();
129
+ const contentHash = createHash("sha256")
130
+ .update(fileBuffer)
131
+ .digest("hex");
132
+ // 3. Dedup check — same hash within org
133
+ const existing = await this.db.document.findFirst({
134
+ where: {
135
+ contentHash,
136
+ id: { not: doc.id },
137
+ status: "ready",
138
+ },
139
+ });
140
+ if (existing) {
141
+ await this.db.document.update({
142
+ where: { id: doc.id },
143
+ data: {
144
+ contentHash,
145
+ status: "ready",
146
+ nodeId: existing.nodeId,
147
+ },
148
+ });
149
+ return;
150
+ }
151
+ // 4. Generate signed read URL for OCR
152
+ const [readUrl] = await gcsFile.getSignedUrl({
153
+ version: "v4",
154
+ action: "read",
155
+ expires: Date.now() + SIGNED_URL_EXPIRY_MS,
156
+ });
157
+ // 5. OCR
158
+ const ocrResult = await this.ocrService.extractText(readUrl, this.tenantCtx.organizationId);
159
+ // Count pages from --- separators
160
+ const pageCount = ocrResult.text.split("\n---\n").length;
161
+ // 6. Create node with extracted text
162
+ const node = await this.nodeService.create({
163
+ type: "document",
164
+ title: doc.fileName,
165
+ content: ocrResult.text,
166
+ createdBy: doc.createdBy,
167
+ status: "approved",
168
+ metadata: {
169
+ sourceDocumentId: doc.id,
170
+ ocrModel: ocrResult.model,
171
+ mimeType: doc.mimeType,
172
+ },
173
+ });
174
+ // 7. Chunk the node
175
+ try {
176
+ await this.chunkService.chunkAndEmbed(node.id, doc.fileName, ocrResult.text);
177
+ }
178
+ catch (err) {
179
+ console.error(`Chunking failed for document ${doc.id}:`, err);
180
+ // Non-fatal — the node still exists
181
+ }
182
+ // 8. Apply provenance tags
183
+ try {
184
+ await this.tagService.applyTags(node.id, [
185
+ {
186
+ tagName: "source_document",
187
+ valueText: doc.id,
188
+ attrs: {
189
+ fileName: doc.fileName,
190
+ mimeType: doc.mimeType,
191
+ storagePath: doc.storagePath,
192
+ },
193
+ },
194
+ {
195
+ tagName: "source_file_format",
196
+ valueText: MIME_TO_EXT[doc.mimeType] ?? "unknown",
197
+ },
198
+ ], "document-service");
199
+ }
200
+ catch (err) {
201
+ console.error(`Tagging failed for document ${doc.id}:`, err);
202
+ // Non-fatal — the node still exists
203
+ }
204
+ // 9. Update document record
205
+ await this.db.document.update({
206
+ where: { id: doc.id },
207
+ data: {
208
+ contentHash,
209
+ status: "ready",
210
+ ocrModel: ocrResult.model,
211
+ ocrTokensIn: ocrResult.tokensIn,
212
+ ocrTokensOut: ocrResult.tokensOut,
213
+ pageCount,
214
+ nodeId: node.id,
215
+ },
216
+ });
217
+ }
218
+ catch (err) {
219
+ // Mark as failed
220
+ const errorMessage = err instanceof Error ? err.message : String(err);
221
+ await this.db.document.update({
222
+ where: { id: doc.id },
223
+ data: {
224
+ status: "failed",
225
+ errorMessage,
226
+ },
227
+ });
228
+ throw err;
229
+ }
230
+ }
231
+ /**
232
+ * List documents, optionally filtered by IDs or status.
233
+ */
234
+ async listDocuments(opts) {
235
+ const where = {};
236
+ if (opts?.ids?.length) {
237
+ where.id = { in: opts.ids };
238
+ }
239
+ if (opts?.status) {
240
+ where.status = opts.status;
241
+ }
242
+ return this.db.document.findMany({
243
+ where,
244
+ orderBy: { createdAt: "desc" },
245
+ });
246
+ }
247
+ /**
248
+ * Generate a signed download URL for a document's original file.
249
+ */
250
+ async getDownloadUrl(documentId) {
251
+ const doc = await this.db.document.findFirst({
252
+ where: { id: documentId },
253
+ });
254
+ if (!doc) {
255
+ throw new Error(`Document ${documentId} not found`);
256
+ }
257
+ const bucket = this.storage.bucket(this.bucketName);
258
+ const gcsFile = bucket.file(doc.storagePath);
259
+ const [downloadUrl] = await gcsFile.getSignedUrl({
260
+ version: "v4",
261
+ action: "read",
262
+ expires: Date.now() + SIGNED_URL_EXPIRY_MS,
263
+ responseDisposition: `attachment; filename="${doc.fileName}"`,
264
+ });
265
+ return {
266
+ downloadUrl,
267
+ fileName: doc.fileName,
268
+ mimeType: doc.mimeType,
269
+ expiresIn: SIGNED_URL_EXPIRY_MS / 1000,
270
+ };
271
+ }
272
+ }
273
+ //# sourceMappingURL=document-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-service.js","sourceRoot":"","sources":["../../src/services/document-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=document-service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-service.test.d.ts","sourceRoot":"","sources":["../../src/services/document-service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,289 @@
1
+ import { describe, expect, it, vi, beforeEach } from "vitest";
2
+ import { DocumentService } from "./document-service.js";
3
+ // ── Mock factories ──────────────────────────────────────────────────────────
4
+ function mockPrisma() {
5
+ return {
6
+ document: {
7
+ create: vi.fn(),
8
+ update: vi.fn(),
9
+ findFirst: vi.fn(),
10
+ findMany: vi.fn(),
11
+ },
12
+ };
13
+ }
14
+ function mockStorage() {
15
+ const signedUrl = "https://storage.googleapis.com/signed-url";
16
+ const fileObj = {
17
+ getSignedUrl: vi.fn().mockResolvedValue([signedUrl]),
18
+ exists: vi.fn().mockResolvedValue([true]),
19
+ download: vi.fn().mockResolvedValue([Buffer.from("file-content")]),
20
+ };
21
+ return {
22
+ bucket: vi.fn().mockReturnValue({
23
+ file: vi.fn().mockReturnValue(fileObj),
24
+ }),
25
+ _file: fileObj,
26
+ _signedUrl: signedUrl,
27
+ };
28
+ }
29
+ function mockOcrService() {
30
+ return {
31
+ extractText: vi.fn().mockResolvedValue({
32
+ text: "# Doc Title\n\nExtracted content\n\n---\n\nPage 2 content",
33
+ tokensIn: 100,
34
+ tokensOut: 50,
35
+ model: "mistral-ocr-latest",
36
+ }),
37
+ };
38
+ }
39
+ function mockNodeService() {
40
+ return {
41
+ create: vi.fn().mockResolvedValue({
42
+ id: "node-1",
43
+ type: "document",
44
+ title: "test.pdf",
45
+ content: "extracted text",
46
+ }),
47
+ };
48
+ }
49
+ function mockChunkService() {
50
+ return {
51
+ chunkAndEmbed: vi.fn().mockResolvedValue(undefined),
52
+ };
53
+ }
54
+ function mockTagService() {
55
+ return {
56
+ applyTags: vi.fn().mockResolvedValue([]),
57
+ };
58
+ }
59
+ const TENANT = { organizationId: "org-123" };
60
+ const BUCKET_NAME = "test-bucket";
61
+ describe("DocumentService", () => {
62
+ let db;
63
+ let storage;
64
+ let ocrService;
65
+ let nodeService;
66
+ let chunkService;
67
+ let tagService;
68
+ let service;
69
+ beforeEach(() => {
70
+ db = mockPrisma();
71
+ storage = mockStorage();
72
+ ocrService = mockOcrService();
73
+ nodeService = mockNodeService();
74
+ chunkService = mockChunkService();
75
+ tagService = mockTagService();
76
+ service = new DocumentService(db, storage, BUCKET_NAME, ocrService, nodeService, chunkService, tagService, undefined, undefined, TENANT);
77
+ });
78
+ // ── createUploadUrls ──────────────────────────────────────────────────────
79
+ describe("createUploadUrls", () => {
80
+ it("creates document records and returns signed URLs", async () => {
81
+ db.document.create.mockResolvedValue({
82
+ id: "doc-1",
83
+ fileName: "test.pdf",
84
+ mimeType: "application/pdf",
85
+ fileSizeBytes: 1024,
86
+ storagePath: "",
87
+ status: "uploading",
88
+ });
89
+ db.document.update.mockResolvedValue({});
90
+ const results = await service.createUploadUrls([
91
+ {
92
+ fileName: "test.pdf",
93
+ mimeType: "application/pdf",
94
+ sizeBytes: 1024,
95
+ },
96
+ ], "user@example.com");
97
+ expect(results).toHaveLength(1);
98
+ expect(results[0].id).toBe("doc-1");
99
+ expect(results[0].uploadUrl).toBe(storage._signedUrl);
100
+ expect(results[0].fileName).toBe("test.pdf");
101
+ // Verify DB calls
102
+ expect(db.document.create).toHaveBeenCalledOnce();
103
+ expect(db.document.update).toHaveBeenCalledOnce();
104
+ // Verify storage path includes org and doc ID
105
+ const updateCall = db.document.update.mock.calls[0][0];
106
+ expect(updateCall.data.storagePath).toContain("org-123/doc-1/original.pdf");
107
+ });
108
+ it("maps mime types to correct extensions", async () => {
109
+ const mimeMap = {
110
+ "application/pdf": "pdf",
111
+ "image/png": "png",
112
+ "image/jpeg": "jpeg",
113
+ "image/avif": "avif",
114
+ };
115
+ for (const [mime, ext] of Object.entries(mimeMap)) {
116
+ db.document.create.mockResolvedValue({
117
+ id: `doc-${ext}`,
118
+ fileName: `test.${ext}`,
119
+ mimeType: mime,
120
+ });
121
+ db.document.update.mockResolvedValue({});
122
+ await service.createUploadUrls([{ fileName: `test.${ext}`, mimeType: mime, sizeBytes: 100 }], "user");
123
+ const updateCall = db.document.update.mock.calls[db.document.update.mock.calls.length - 1][0];
124
+ expect(updateCall.data.storagePath).toContain(`original.${ext}`);
125
+ }
126
+ });
127
+ });
128
+ // ── processDocuments ──────────────────────────────────────────────────────
129
+ describe("processDocuments", () => {
130
+ it("sets status to processing and fires background work", async () => {
131
+ db.document.findFirst.mockResolvedValue({
132
+ id: "doc-1",
133
+ status: "uploading",
134
+ storagePath: "org-123/doc-1/original.pdf",
135
+ fileName: "test.pdf",
136
+ mimeType: "application/pdf",
137
+ createdBy: "user",
138
+ });
139
+ db.document.update.mockResolvedValue({});
140
+ const result = await service.processDocuments(["doc-1"]);
141
+ expect(result.documents).toHaveLength(1);
142
+ expect(result.documents[0].status).toBe("processing");
143
+ expect(result.warnings).toHaveLength(0);
144
+ });
145
+ it("warns on missing documents", async () => {
146
+ db.document.findFirst.mockResolvedValue(null);
147
+ const result = await service.processDocuments(["nonexistent"]);
148
+ expect(result.warnings).toContain("Document nonexistent not found");
149
+ });
150
+ it("warns on documents not in uploading status", async () => {
151
+ db.document.findFirst.mockResolvedValue({
152
+ id: "doc-1",
153
+ status: "ready",
154
+ });
155
+ const result = await service.processDocuments(["doc-1"]);
156
+ expect(result.warnings[0]).toContain("expected \"uploading\"");
157
+ expect(result.documents[0].status).toBe("ready");
158
+ });
159
+ });
160
+ // ── processDocument ───────────────────────────────────────────────────────
161
+ describe("processDocument", () => {
162
+ const mockDoc = {
163
+ id: "doc-1",
164
+ status: "processing",
165
+ storagePath: "org-123/doc-1/original.pdf",
166
+ fileName: "test.pdf",
167
+ mimeType: "application/pdf",
168
+ createdBy: "user",
169
+ fileSizeBytes: 1024,
170
+ };
171
+ it("runs full OCR → node → chunk → tag → update pipeline", async () => {
172
+ db.document.findFirst
173
+ .mockResolvedValueOnce(mockDoc) // initial lookup
174
+ .mockResolvedValueOnce(null); // dedup check
175
+ db.document.update.mockResolvedValue({});
176
+ await service.processDocument("doc-1");
177
+ // OCR was called
178
+ expect(ocrService.extractText).toHaveBeenCalledOnce();
179
+ // Node was created
180
+ expect(nodeService.create).toHaveBeenCalledWith(expect.objectContaining({
181
+ type: "document",
182
+ title: "test.pdf",
183
+ status: "approved",
184
+ }));
185
+ // Chunks were created
186
+ expect(chunkService.chunkAndEmbed).toHaveBeenCalledWith("node-1", "test.pdf", expect.any(String));
187
+ // Tags were applied
188
+ expect(tagService.applyTags).toHaveBeenCalledWith("node-1", expect.arrayContaining([
189
+ expect.objectContaining({ tagName: "source_document" }),
190
+ expect.objectContaining({ tagName: "source_file_format" }),
191
+ ]), "document-service");
192
+ // Document updated to ready
193
+ const lastUpdate = db.document.update.mock.calls[db.document.update.mock.calls.length - 1][0];
194
+ expect(lastUpdate.data.status).toBe("ready");
195
+ expect(lastUpdate.data.nodeId).toBe("node-1");
196
+ expect(lastUpdate.data.contentHash).toBeDefined();
197
+ expect(lastUpdate.data.ocrModel).toBe("mistral-ocr-latest");
198
+ expect(lastUpdate.data.pageCount).toBe(2); // two pages separated by ---
199
+ });
200
+ it("deduplicates by content hash", async () => {
201
+ db.document.findFirst
202
+ .mockResolvedValueOnce(mockDoc) // initial lookup
203
+ .mockResolvedValueOnce({ id: "existing-doc", nodeId: "existing-node", status: "ready" }); // dedup match
204
+ db.document.update.mockResolvedValue({});
205
+ await service.processDocument("doc-1");
206
+ // OCR should NOT have been called
207
+ expect(ocrService.extractText).not.toHaveBeenCalled();
208
+ // Document should link to existing node
209
+ const updateCall = db.document.update.mock.calls[db.document.update.mock.calls.length - 1][0];
210
+ expect(updateCall.data.nodeId).toBe("existing-node");
211
+ expect(updateCall.data.status).toBe("ready");
212
+ });
213
+ it("marks as failed when GCS file missing", async () => {
214
+ storage._file.exists.mockResolvedValue([false]);
215
+ db.document.findFirst
216
+ .mockResolvedValueOnce(mockDoc);
217
+ db.document.update.mockResolvedValue({});
218
+ await expect(service.processDocument("doc-1")).rejects.toThrow("File not found in GCS");
219
+ // Should have updated status to failed
220
+ const updateCall = db.document.update.mock.calls[0][0];
221
+ expect(updateCall.data.status).toBe("failed");
222
+ expect(updateCall.data.errorMessage).toContain("File not found in GCS");
223
+ });
224
+ it("marks as failed when OCR fails", async () => {
225
+ db.document.findFirst
226
+ .mockResolvedValueOnce(mockDoc)
227
+ .mockResolvedValueOnce(null); // no dedup
228
+ db.document.update.mockResolvedValue({});
229
+ ocrService.extractText.mockRejectedValueOnce(new Error("OCR timeout"));
230
+ await expect(service.processDocument("doc-1")).rejects.toThrow("OCR timeout");
231
+ const updateCall = db.document.update.mock.calls[db.document.update.mock.calls.length - 1][0];
232
+ expect(updateCall.data.status).toBe("failed");
233
+ expect(updateCall.data.errorMessage).toBe("OCR timeout");
234
+ });
235
+ it("throws when document not found", async () => {
236
+ db.document.findFirst.mockResolvedValue(null);
237
+ await expect(service.processDocument("nonexistent")).rejects.toThrow("Document nonexistent not found");
238
+ });
239
+ });
240
+ // ── listDocuments ─────────────────────────────────────────────────────────
241
+ describe("listDocuments", () => {
242
+ it("lists all documents without filters", async () => {
243
+ db.document.findMany.mockResolvedValue([{ id: "doc-1" }]);
244
+ const docs = await service.listDocuments();
245
+ expect(db.document.findMany).toHaveBeenCalledWith({
246
+ where: {},
247
+ orderBy: { createdAt: "desc" },
248
+ });
249
+ expect(docs).toHaveLength(1);
250
+ });
251
+ it("filters by IDs", async () => {
252
+ db.document.findMany.mockResolvedValue([]);
253
+ await service.listDocuments({ ids: ["doc-1", "doc-2"] });
254
+ expect(db.document.findMany).toHaveBeenCalledWith({
255
+ where: { id: { in: ["doc-1", "doc-2"] } },
256
+ orderBy: { createdAt: "desc" },
257
+ });
258
+ });
259
+ it("filters by status", async () => {
260
+ db.document.findMany.mockResolvedValue([]);
261
+ await service.listDocuments({ status: "ready" });
262
+ expect(db.document.findMany).toHaveBeenCalledWith({
263
+ where: { status: "ready" },
264
+ orderBy: { createdAt: "desc" },
265
+ });
266
+ });
267
+ });
268
+ // ── getDownloadUrl ────────────────────────────────────────────────────────
269
+ describe("getDownloadUrl", () => {
270
+ it("generates a signed read URL", async () => {
271
+ db.document.findFirst.mockResolvedValue({
272
+ id: "doc-1",
273
+ fileName: "test.pdf",
274
+ mimeType: "application/pdf",
275
+ storagePath: "org-123/doc-1/original.pdf",
276
+ });
277
+ const result = await service.getDownloadUrl("doc-1");
278
+ expect(result.downloadUrl).toBe(storage._signedUrl);
279
+ expect(result.fileName).toBe("test.pdf");
280
+ expect(result.mimeType).toBe("application/pdf");
281
+ expect(result.expiresIn).toBe(900); // 15 minutes
282
+ });
283
+ it("throws when document not found", async () => {
284
+ db.document.findFirst.mockResolvedValue(null);
285
+ await expect(service.getDownloadUrl("nonexistent")).rejects.toThrow("Document nonexistent not found");
286
+ });
287
+ });
288
+ });
289
+ //# sourceMappingURL=document-service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-service.test.js","sourceRoot":"","sources":["../../src/services/document-service.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * OCR text extraction via the AIShield proxy (Mistral OCR model).
3
+ *
4
+ * Calls the OpenAI-compatible chat completions endpoint at AIShield,
5
+ * passing a signed GCS document URL as a `document_url` content part.
6
+ * The model returns extracted text as structured markdown.
7
+ */
8
+ export interface OcrResult {
9
+ text: string;
10
+ tokensIn: number;
11
+ tokensOut: number;
12
+ model: string;
13
+ }
14
+ export declare class OcrService {
15
+ private baseUrl;
16
+ private apiKey;
17
+ private model;
18
+ constructor(baseUrl: string, apiKey: string, model?: string);
19
+ extractText(documentUrl: string, organizationId: string): Promise<OcrResult>;
20
+ }
21
+ //# sourceMappingURL=ocr-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocr-service.d.ts","sourceRoot":"","sources":["../../src/services/ocr-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,qBAAa,UAAU;IAEnB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,KAAK;gBAFL,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAA6B;IAGxC,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,CAAC;CAiDtB"}