@echofiles/echo-pdf 0.4.1 → 0.4.3

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.
Files changed (72) hide show
  1. package/README.md +302 -11
  2. package/bin/echo-pdf.js +176 -8
  3. package/bin/lib/http.js +26 -1
  4. package/dist/agent-defaults.d.ts +3 -0
  5. package/dist/agent-defaults.js +18 -0
  6. package/dist/auth.d.ts +18 -0
  7. package/dist/auth.js +36 -0
  8. package/dist/core/index.d.ts +50 -0
  9. package/dist/core/index.js +7 -0
  10. package/dist/file-ops.d.ts +11 -0
  11. package/dist/file-ops.js +36 -0
  12. package/dist/file-store-do.d.ts +36 -0
  13. package/dist/file-store-do.js +298 -0
  14. package/dist/file-utils.d.ts +6 -0
  15. package/dist/file-utils.js +36 -0
  16. package/dist/http-error.d.ts +9 -0
  17. package/dist/http-error.js +14 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1 -0
  20. package/dist/local/index.d.ts +135 -0
  21. package/dist/local/index.js +555 -0
  22. package/dist/mcp-server.d.ts +3 -0
  23. package/dist/mcp-server.js +124 -0
  24. package/dist/node/pdfium-local.d.ts +8 -0
  25. package/dist/node/pdfium-local.js +147 -0
  26. package/dist/node/semantic-local.d.ts +16 -0
  27. package/dist/node/semantic-local.js +113 -0
  28. package/dist/pdf-agent.d.ts +18 -0
  29. package/dist/pdf-agent.js +217 -0
  30. package/dist/pdf-config.d.ts +4 -0
  31. package/dist/pdf-config.js +140 -0
  32. package/dist/pdf-storage.d.ts +8 -0
  33. package/dist/pdf-storage.js +86 -0
  34. package/dist/pdf-types.d.ts +83 -0
  35. package/dist/pdf-types.js +1 -0
  36. package/dist/pdfium-engine.d.ts +9 -0
  37. package/dist/pdfium-engine.js +180 -0
  38. package/dist/provider-client.d.ts +20 -0
  39. package/dist/provider-client.js +173 -0
  40. package/dist/provider-keys.d.ts +10 -0
  41. package/dist/provider-keys.js +27 -0
  42. package/dist/r2-file-store.d.ts +20 -0
  43. package/dist/r2-file-store.js +176 -0
  44. package/dist/response-schema.d.ts +15 -0
  45. package/dist/response-schema.js +159 -0
  46. package/dist/tool-registry.d.ts +16 -0
  47. package/dist/tool-registry.js +175 -0
  48. package/dist/types.d.ts +91 -0
  49. package/dist/types.js +1 -0
  50. package/dist/worker.d.ts +7 -0
  51. package/dist/worker.js +386 -0
  52. package/package.json +34 -5
  53. package/wrangler.toml +1 -1
  54. package/src/agent-defaults.ts +0 -25
  55. package/src/file-ops.ts +0 -50
  56. package/src/file-store-do.ts +0 -349
  57. package/src/file-utils.ts +0 -43
  58. package/src/http-error.ts +0 -21
  59. package/src/index.ts +0 -415
  60. package/src/mcp-server.ts +0 -171
  61. package/src/pdf-agent.ts +0 -252
  62. package/src/pdf-config.ts +0 -143
  63. package/src/pdf-storage.ts +0 -109
  64. package/src/pdf-types.ts +0 -85
  65. package/src/pdfium-engine.ts +0 -207
  66. package/src/provider-client.ts +0 -176
  67. package/src/provider-keys.ts +0 -44
  68. package/src/r2-file-store.ts +0 -195
  69. package/src/response-schema.ts +0 -182
  70. package/src/tool-registry.ts +0 -203
  71. package/src/types.ts +0 -40
  72. package/src/wasm.d.ts +0 -4
package/src/pdf-agent.ts DELETED
@@ -1,252 +0,0 @@
1
- import type { Env, FileStore, ReturnMode } from "./types"
2
- import type { AgentTraceEvent, EchoPdfConfig, PdfOperationRequest } from "./pdf-types"
3
- import { resolveModelForProvider, resolveProviderAlias } from "./agent-defaults"
4
- import { fromBase64, normalizeReturnMode, toDataUrl } from "./file-utils"
5
- import { badRequest, notFound, unprocessable } from "./http-error"
6
- import { extractPdfPageText, getPdfPageCount, renderPdfPageToPng, toBytes } from "./pdfium-engine"
7
- import { visionRecognize } from "./provider-client"
8
-
9
- interface RuntimeOptions {
10
- readonly trace?: (event: AgentTraceEvent) => void
11
- readonly fileStore: FileStore
12
- }
13
-
14
- const traceStep = (
15
- opts: RuntimeOptions,
16
- phase: AgentTraceEvent["phase"],
17
- name: string,
18
- payload?: unknown,
19
- level?: AgentTraceEvent["level"]
20
- ): void => {
21
- if (!opts.trace) return
22
- opts.trace({ kind: "step", phase, name, payload, level })
23
- }
24
-
25
- const ensurePages = (pages: ReadonlyArray<number>, pageCount: number, maxPages: number): number[] => {
26
- if (pages.length === 0) throw badRequest("PAGES_REQUIRED", "At least one page is required")
27
- if (pages.length > maxPages) {
28
- throw badRequest("TOO_MANY_PAGES", `Page count exceeds maxPagesPerRequest (${maxPages})`, {
29
- maxPagesPerRequest: maxPages,
30
- providedPages: pages.length,
31
- })
32
- }
33
- for (const page of pages) {
34
- if (!Number.isInteger(page) || page < 1 || page > pageCount) {
35
- throw badRequest("PAGE_OUT_OF_RANGE", `Page ${page} out of range 1..${pageCount}`, {
36
- page,
37
- min: 1,
38
- max: pageCount,
39
- })
40
- }
41
- }
42
- return [...new Set(pages)].sort((a, b) => a - b)
43
- }
44
-
45
- export const ingestPdfFromPayload = async (
46
- config: EchoPdfConfig,
47
- input: {
48
- readonly fileId?: string
49
- readonly url?: string
50
- readonly base64?: string
51
- readonly filename?: string
52
- },
53
- opts: RuntimeOptions
54
- ): Promise<{ id: string; filename: string; bytes: Uint8Array }> => {
55
- if (input.fileId) {
56
- const existing = await opts.fileStore.get(input.fileId)
57
- if (!existing) {
58
- throw notFound("FILE_NOT_FOUND", `File not found: ${input.fileId}`, { fileId: input.fileId })
59
- }
60
- return {
61
- id: existing.id,
62
- filename: existing.filename,
63
- bytes: existing.bytes,
64
- }
65
- }
66
-
67
- let bytes: Uint8Array | null = null
68
- let filename = input.filename ?? "document.pdf"
69
-
70
- if (input.url) {
71
- traceStep(opts, "start", "file.fetch.url", { url: input.url })
72
- try {
73
- bytes = await toBytes(input.url)
74
- } catch (error) {
75
- throw badRequest("URL_FETCH_FAILED", `Unable to fetch PDF from url: ${error instanceof Error ? error.message : String(error)}`)
76
- }
77
- try {
78
- const u = new URL(input.url)
79
- filename = decodeURIComponent(u.pathname.split("/").pop() || filename)
80
- } catch {
81
- // ignore URL parse failure
82
- }
83
- traceStep(opts, "end", "file.fetch.url", { sizeBytes: bytes.byteLength })
84
- } else if (input.base64) {
85
- traceStep(opts, "start", "file.decode.base64")
86
- bytes = fromBase64(input.base64)
87
- traceStep(opts, "end", "file.decode.base64", { sizeBytes: bytes.byteLength })
88
- }
89
-
90
- if (!bytes) {
91
- throw badRequest("MISSING_FILE_INPUT", "Missing file input. Provide fileId, url or base64")
92
- }
93
- if (bytes.byteLength > config.service.maxPdfBytes) {
94
- throw badRequest("PDF_TOO_LARGE", `PDF exceeds max size (${config.service.maxPdfBytes} bytes)`, {
95
- maxPdfBytes: config.service.maxPdfBytes,
96
- sizeBytes: bytes.byteLength,
97
- })
98
- }
99
-
100
- const meta = await opts.fileStore.put({
101
- filename,
102
- mimeType: "application/pdf",
103
- bytes,
104
- })
105
- traceStep(opts, "end", "file.stored", { fileId: meta.id, sizeBytes: meta.sizeBytes })
106
- return {
107
- id: meta.id,
108
- filename: meta.filename,
109
- bytes,
110
- }
111
- }
112
-
113
- const resolveReturnMode = (value: ReturnMode | undefined): ReturnMode => normalizeReturnMode(value)
114
-
115
- const stripCodeFences = (value: string): string => {
116
- const text = value.trim()
117
- const fenced = text.match(/^```[a-zA-Z0-9_-]*\n([\s\S]*?)\n```$/)
118
- return typeof fenced?.[1] === "string" ? fenced[1].trim() : text
119
- }
120
-
121
- const extractTabularLatex = (value: string): string => {
122
- const text = stripCodeFences(value)
123
- const blocks = text.match(/\\begin\{tabular\}[\s\S]*?\\end\{tabular\}/g)
124
- if (!blocks || blocks.length === 0) return ""
125
- return blocks.map((b) => b.trim()).join("\n\n")
126
- }
127
-
128
- export const runPdfAgent = async (
129
- config: EchoPdfConfig,
130
- env: Env,
131
- request: PdfOperationRequest,
132
- opts: RuntimeOptions
133
- ): Promise<unknown> => {
134
- traceStep(opts, "start", "pdf.operation", { operation: request.operation })
135
- const file = await ingestPdfFromPayload(config, request, opts)
136
- const pageCount = await getPdfPageCount(config, file.bytes)
137
- traceStep(opts, "log", "pdf.meta", { fileId: file.id, pageCount })
138
-
139
- const pages = ensurePages(request.pages, pageCount, config.service.maxPagesPerRequest)
140
- const scale = request.renderScale ?? config.service.defaultRenderScale
141
- const returnMode = resolveReturnMode(request.returnMode)
142
-
143
- if (request.operation === "extract_pages") {
144
- const images: Array<{ page: number; mimeType: string; data?: string; fileId?: string; url?: string | null }> = []
145
- for (const page of pages) {
146
- traceStep(opts, "start", "render.page", { page })
147
- const rendered = await renderPdfPageToPng(config, file.bytes, page - 1, scale)
148
- if (returnMode === "file_id") {
149
- const stored = await opts.fileStore.put({
150
- filename: `${file.filename}-p${page}.png`,
151
- mimeType: "image/png",
152
- bytes: rendered.png,
153
- })
154
- images.push({ page, mimeType: "image/png", fileId: stored.id })
155
- } else if (returnMode === "url") {
156
- const stored = await opts.fileStore.put({
157
- filename: `${file.filename}-p${page}.png`,
158
- mimeType: "image/png",
159
- bytes: rendered.png,
160
- })
161
- images.push({
162
- page,
163
- mimeType: "image/png",
164
- fileId: stored.id,
165
- url: `/api/files/get?fileId=${encodeURIComponent(stored.id)}`,
166
- })
167
- } else {
168
- images.push({
169
- page,
170
- mimeType: "image/png",
171
- data: toDataUrl(rendered.png, "image/png"),
172
- })
173
- }
174
- traceStep(opts, "end", "render.page", { page, width: rendered.width, height: rendered.height })
175
- }
176
- const result = { fileId: file.id, pageCount, returnMode, images }
177
- traceStep(opts, "end", "pdf.operation", { operation: request.operation })
178
- return result
179
- }
180
-
181
- const providerAlias = resolveProviderAlias(config, request.provider)
182
- const model = resolveModelForProvider(config, providerAlias, request.model)
183
- if (!model) {
184
- throw badRequest("MODEL_REQUIRED", "model is required for OCR or table extraction; set agent.defaultModel")
185
- }
186
-
187
- if (request.operation === "ocr_pages") {
188
- const results: Array<{ page: number; text: string }> = []
189
- for (const page of pages) {
190
- traceStep(opts, "start", "ocr.page", { page })
191
- const rendered = await renderPdfPageToPng(config, file.bytes, page - 1, scale)
192
- const imageDataUrl = toDataUrl(rendered.png, "image/png")
193
- const fallbackText = await extractPdfPageText(config, file.bytes, page - 1)
194
- const prompt = request.prompt?.trim() || config.agent.ocrPrompt
195
- const llmText = await visionRecognize({
196
- config,
197
- env,
198
- providerAlias,
199
- model,
200
- prompt,
201
- imageDataUrl,
202
- runtimeApiKeys: request.providerApiKeys,
203
- })
204
- const text = stripCodeFences(llmText || fallbackText || "")
205
- results.push({ page, text })
206
- traceStep(opts, "end", "ocr.page", { page, chars: text.length })
207
- }
208
- const result = {
209
- fileId: file.id,
210
- pageCount,
211
- provider: providerAlias,
212
- model,
213
- pages: results,
214
- }
215
- traceStep(opts, "end", "pdf.operation", { operation: request.operation })
216
- return result
217
- }
218
-
219
- const tables: Array<{ page: number; latex: string }> = []
220
- for (const page of pages) {
221
- traceStep(opts, "start", "table.page", { page })
222
- const rendered = await renderPdfPageToPng(config, file.bytes, page - 1, scale)
223
- const imageDataUrl = toDataUrl(rendered.png, "image/png")
224
- const prompt = request.prompt?.trim() || config.agent.tablePrompt
225
- const rawLatex = await visionRecognize({
226
- config,
227
- env,
228
- providerAlias,
229
- model,
230
- prompt,
231
- imageDataUrl,
232
- runtimeApiKeys: request.providerApiKeys,
233
- })
234
- const latex = extractTabularLatex(rawLatex)
235
- if (!latex) {
236
- throw unprocessable("TABLE_LATEX_MISSING", `table extraction did not return valid LaTeX tabular for page ${page}`, {
237
- page,
238
- })
239
- }
240
- tables.push({ page, latex })
241
- traceStep(opts, "end", "table.page", { page, chars: latex.length })
242
- }
243
- const result = {
244
- fileId: file.id,
245
- pageCount,
246
- provider: providerAlias,
247
- model,
248
- pages: tables,
249
- }
250
- traceStep(opts, "end", "pdf.operation", { operation: request.operation })
251
- return result
252
- }
package/src/pdf-config.ts DELETED
@@ -1,143 +0,0 @@
1
- import rawConfig from "../echo-pdf.config.json"
2
- import type { Env, JsonObject, JsonValue } from "./types"
3
- import type { EchoPdfConfig } from "./pdf-types"
4
-
5
- const ENV_PATTERN = /\$\{([A-Z0-9_]+)\}/g
6
-
7
- const isObject = (value: unknown): value is Record<string, unknown> =>
8
- typeof value === "object" && value !== null && !Array.isArray(value)
9
-
10
- const interpolateEnv = (input: string, env: Env): string =>
11
- input.replace(ENV_PATTERN, (_, name: string) => {
12
- const value = env[name]
13
- return typeof value === "string" ? value : `\${${name}}`
14
- })
15
-
16
- const resolveEnvRefs = (value: JsonValue, env: Env): JsonValue => {
17
- if (typeof value === "string") return interpolateEnv(value, env)
18
- if (Array.isArray(value)) return value.map((item) => resolveEnvRefs(item, env))
19
- if (isObject(value)) {
20
- const out: JsonObject = {}
21
- for (const [key, nested] of Object.entries(value)) {
22
- out[key] = resolveEnvRefs(nested as JsonValue, env)
23
- }
24
- return out
25
- }
26
- return value
27
- }
28
-
29
- const validateConfig = (config: EchoPdfConfig): EchoPdfConfig => {
30
- if (!config.service?.name) throw new Error("service.name is required")
31
- if (!config.pdfium?.wasmUrl) throw new Error("pdfium.wasmUrl is required")
32
- if (!config.service?.storage) throw new Error("service.storage is required")
33
- if (
34
- typeof config.service.publicBaseUrl === "string" &&
35
- config.service.publicBaseUrl.length > 0 &&
36
- !/^https?:\/\//.test(config.service.publicBaseUrl)
37
- ) {
38
- throw new Error("service.publicBaseUrl must start with http:// or https://")
39
- }
40
- if (typeof config.service.fileGet?.cacheTtlSeconds === "number" && config.service.fileGet.cacheTtlSeconds < 0) {
41
- throw new Error("service.fileGet.cacheTtlSeconds must be >= 0")
42
- }
43
- if (!Number.isFinite(config.service.storage.maxFileBytes) || config.service.storage.maxFileBytes <= 0) {
44
- throw new Error("service.storage.maxFileBytes must be positive")
45
- }
46
- if (config.service.storage.maxFileBytes < config.service.maxPdfBytes) {
47
- throw new Error("service.storage.maxFileBytes must be >= service.maxPdfBytes")
48
- }
49
- if (!Number.isFinite(config.service.storage.maxTotalBytes) || config.service.storage.maxTotalBytes <= 0) {
50
- throw new Error("service.storage.maxTotalBytes must be positive")
51
- }
52
- if (config.service.storage.maxTotalBytes < config.service.storage.maxFileBytes) {
53
- throw new Error("service.storage.maxTotalBytes must be >= maxFileBytes")
54
- }
55
- if (!Number.isFinite(config.service.storage.ttlHours) || config.service.storage.ttlHours <= 0) {
56
- throw new Error("service.storage.ttlHours must be positive")
57
- }
58
- if (!Number.isFinite(config.service.storage.cleanupBatchSize) || config.service.storage.cleanupBatchSize <= 0) {
59
- throw new Error("service.storage.cleanupBatchSize must be positive")
60
- }
61
- if (!config.agent?.defaultProvider) throw new Error("agent.defaultProvider is required")
62
- if (!config.providers?.[config.agent.defaultProvider]) {
63
- throw new Error(`default provider "${config.agent.defaultProvider}" missing`)
64
- }
65
- if (typeof config.agent.defaultModel !== "string") {
66
- throw new Error("agent.defaultModel must be a string")
67
- }
68
- return config
69
- }
70
-
71
- export const loadEchoPdfConfig = (env: Env): EchoPdfConfig => {
72
- const fromEnv = env.ECHO_PDF_CONFIG_JSON?.trim()
73
- const configJson = fromEnv ? JSON.parse(fromEnv) : rawConfig
74
- const resolved = resolveEnvRefs(configJson as unknown as JsonValue, env) as unknown as EchoPdfConfig
75
-
76
- const providerOverride = env.ECHO_PDF_DEFAULT_PROVIDER
77
- const modelOverride = env.ECHO_PDF_DEFAULT_MODEL
78
- const publicBaseUrlOverride = env.ECHO_PDF_PUBLIC_BASE_URL
79
- const fileGetAuthHeaderOverride = env.ECHO_PDF_FILE_GET_AUTH_HEADER
80
- const fileGetAuthEnvOverride = env.ECHO_PDF_FILE_GET_AUTH_ENV
81
- const fileGetCacheTtlOverride = env.ECHO_PDF_FILE_GET_CACHE_TTL_SECONDS
82
- const withOverrides: EchoPdfConfig = {
83
- ...resolved,
84
- service: {
85
- ...resolved.service,
86
- publicBaseUrl:
87
- typeof publicBaseUrlOverride === "string" && publicBaseUrlOverride.trim().length > 0
88
- ? publicBaseUrlOverride.trim()
89
- : resolved.service.publicBaseUrl,
90
- fileGet: {
91
- authHeader:
92
- typeof fileGetAuthHeaderOverride === "string" && fileGetAuthHeaderOverride.trim().length > 0
93
- ? fileGetAuthHeaderOverride.trim()
94
- : resolved.service.fileGet?.authHeader,
95
- authEnv:
96
- typeof fileGetAuthEnvOverride === "string" && fileGetAuthEnvOverride.trim().length > 0
97
- ? fileGetAuthEnvOverride.trim()
98
- : resolved.service.fileGet?.authEnv,
99
- cacheTtlSeconds: (() => {
100
- if (typeof fileGetCacheTtlOverride === "string" && fileGetCacheTtlOverride.trim().length > 0) {
101
- const value = Number(fileGetCacheTtlOverride)
102
- return Number.isFinite(value) && value >= 0 ? Math.floor(value) : resolved.service.fileGet?.cacheTtlSeconds
103
- }
104
- return resolved.service.fileGet?.cacheTtlSeconds
105
- })(),
106
- },
107
- },
108
- agent: {
109
- ...resolved.agent,
110
- defaultProvider:
111
- typeof providerOverride === "string" && providerOverride.trim().length > 0
112
- ? providerOverride.trim()
113
- : resolved.agent.defaultProvider,
114
- defaultModel:
115
- typeof modelOverride === "string" && modelOverride.trim().length > 0
116
- ? modelOverride.trim()
117
- : resolved.agent.defaultModel,
118
- },
119
- }
120
-
121
- return validateConfig(withOverrides)
122
- }
123
-
124
- export const readRequiredEnv = (env: Env, key: string): string => {
125
- const read = (name: string): string | null => {
126
- const value = env[name]
127
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : null
128
- }
129
- const direct = read(key)
130
- if (direct) return direct
131
-
132
- // Backward compatibility: allow *_KEY and *_API_KEY aliases.
133
- if (key.endsWith("_API_KEY")) {
134
- const alt = read(key.replace(/_API_KEY$/, "_KEY"))
135
- if (alt) return alt
136
- }
137
- if (key.endsWith("_KEY")) {
138
- const alt = read(key.replace(/_KEY$/, "_API_KEY"))
139
- if (alt) return alt
140
- }
141
-
142
- throw new Error(`Missing required env var "${key}"`)
143
- }
@@ -1,109 +0,0 @@
1
- import { DurableObjectFileStore } from "./file-store-do"
2
- import { R2FileStore } from "./r2-file-store"
3
- import type { EchoPdfConfig } from "./pdf-types"
4
- import type { Env, FileStore, StoredFileMeta, StoredFileRecord } from "./types"
5
-
6
- class InMemoryFileStore implements FileStore {
7
- private readonly store = new Map<string, StoredFileRecord>()
8
-
9
- async put(input: {
10
- readonly filename: string
11
- readonly mimeType: string
12
- readonly bytes: Uint8Array
13
- }): Promise<StoredFileMeta> {
14
- const id = crypto.randomUUID()
15
- const record: StoredFileRecord = {
16
- id,
17
- filename: input.filename,
18
- mimeType: input.mimeType,
19
- sizeBytes: input.bytes.byteLength,
20
- createdAt: new Date().toISOString(),
21
- bytes: input.bytes,
22
- }
23
- this.store.set(id, record)
24
- return this.toMeta(record)
25
- }
26
-
27
- async get(fileId: string): Promise<StoredFileRecord | null> {
28
- return this.store.get(fileId) ?? null
29
- }
30
-
31
- async list(): Promise<ReadonlyArray<StoredFileMeta>> {
32
- return [...this.store.values()].map((record) => this.toMeta(record))
33
- }
34
-
35
- async delete(fileId: string): Promise<boolean> {
36
- return this.store.delete(fileId)
37
- }
38
-
39
- private toMeta(record: StoredFileRecord): StoredFileMeta {
40
- return {
41
- id: record.id,
42
- filename: record.filename,
43
- mimeType: record.mimeType,
44
- sizeBytes: record.sizeBytes,
45
- createdAt: record.createdAt,
46
- }
47
- }
48
- }
49
-
50
- const fallbackStore = new InMemoryFileStore()
51
- const DO_SAFE_MAX_FILE_BYTES = 1_200_000
52
-
53
- export interface RuntimeFileStoreBundle {
54
- readonly store: FileStore
55
- stats: () => Promise<unknown>
56
- cleanup: () => Promise<unknown>
57
- }
58
-
59
- export const getRuntimeFileStore = (env: Env, config: EchoPdfConfig): RuntimeFileStoreBundle => {
60
- if (env.FILE_STORE_BUCKET) {
61
- const store = new R2FileStore(env.FILE_STORE_BUCKET, config.service.storage)
62
- return {
63
- store,
64
- stats: async () => store.stats(),
65
- cleanup: async () => store.cleanup(),
66
- }
67
- }
68
- if (env.FILE_STORE_DO) {
69
- if (config.service.storage.maxFileBytes > DO_SAFE_MAX_FILE_BYTES) {
70
- throw new Error(
71
- `service.storage.maxFileBytes=${config.service.storage.maxFileBytes} exceeds DO backend limit ${DO_SAFE_MAX_FILE_BYTES}; bind FILE_STORE_BUCKET (R2) or reduce maxFileBytes`
72
- )
73
- }
74
- const store = new DurableObjectFileStore(env.FILE_STORE_DO, config.service.storage)
75
- return {
76
- store,
77
- stats: async () => store.stats(),
78
- cleanup: async () => store.cleanup(),
79
- }
80
- }
81
-
82
- return {
83
- store: fallbackStore,
84
- stats: async () => {
85
- const files = await fallbackStore.list()
86
- const totalBytes = files.reduce((sum, file) => sum + file.sizeBytes, 0)
87
- return {
88
- backend: "memory",
89
- policy: config.service.storage,
90
- stats: {
91
- fileCount: files.length,
92
- totalBytes,
93
- },
94
- }
95
- },
96
- cleanup: async () => ({
97
- backend: "memory",
98
- deletedExpired: 0,
99
- deletedEvicted: 0,
100
- stats: await (async () => {
101
- const files = await fallbackStore.list()
102
- return {
103
- fileCount: files.length,
104
- totalBytes: files.reduce((sum, file) => sum + file.sizeBytes, 0),
105
- }
106
- })(),
107
- }),
108
- }
109
- }
package/src/pdf-types.ts DELETED
@@ -1,85 +0,0 @@
1
- import type { ProviderType, ReturnMode } from "./types"
2
-
3
- export interface EchoPdfProviderConfig {
4
- readonly type: ProviderType
5
- readonly apiKeyEnv: string
6
- readonly baseUrl?: string
7
- readonly headers?: Record<string, string>
8
- readonly timeoutMs?: number
9
- readonly endpoints?: {
10
- readonly chatCompletionsPath?: string
11
- readonly modelsPath?: string
12
- }
13
- }
14
-
15
- export interface StoragePolicy {
16
- readonly maxFileBytes: number
17
- readonly maxTotalBytes: number
18
- readonly ttlHours: number
19
- readonly cleanupBatchSize: number
20
- }
21
-
22
- export interface EchoPdfConfig {
23
- readonly service: {
24
- readonly name: string
25
- readonly publicBaseUrl?: string
26
- readonly fileGet?: {
27
- readonly authHeader?: string
28
- readonly authEnv?: string
29
- readonly cacheTtlSeconds?: number
30
- }
31
- readonly maxPdfBytes: number
32
- readonly maxPagesPerRequest: number
33
- readonly defaultRenderScale: number
34
- readonly storage: StoragePolicy
35
- }
36
- readonly pdfium: {
37
- readonly wasmUrl: string
38
- }
39
- readonly agent: {
40
- readonly defaultProvider: string
41
- readonly defaultModel: string
42
- readonly ocrPrompt: string
43
- readonly tablePrompt: string
44
- }
45
- readonly providers: Record<string, EchoPdfProviderConfig>
46
- readonly mcp: {
47
- readonly serverName: string
48
- readonly version: string
49
- readonly authHeader?: string
50
- readonly authEnv?: string
51
- }
52
- }
53
-
54
- export interface AgentTraceEvent {
55
- readonly kind: "step"
56
- readonly phase: "start" | "end" | "log"
57
- readonly name: string
58
- readonly level?: "info" | "error"
59
- readonly payload?: unknown
60
- }
61
-
62
- export interface PdfOperationRequest {
63
- readonly operation: "extract_pages" | "ocr_pages" | "tables_to_latex"
64
- readonly fileId?: string
65
- readonly url?: string
66
- readonly base64?: string
67
- readonly filename?: string
68
- readonly pages: ReadonlyArray<number>
69
- readonly renderScale?: number
70
- readonly provider?: string
71
- readonly model: string
72
- readonly providerApiKeys?: Record<string, string>
73
- readonly returnMode?: ReturnMode
74
- readonly prompt?: string
75
- }
76
-
77
- export interface ToolSchema {
78
- readonly name: string
79
- readonly description: string
80
- readonly inputSchema: Record<string, unknown>
81
- readonly source: {
82
- readonly kind: "local"
83
- readonly toolName: string
84
- }
85
- }