@retinue/agentkit 0.1.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.
- package/LICENSE +22 -0
- package/README.md +310 -0
- package/dist/adapters/bullmq/consumer.d.ts +33 -0
- package/dist/adapters/bullmq/consumer.js +41 -0
- package/dist/adapters/bullmq/dispatcher.d.ts +74 -0
- package/dist/adapters/bullmq/dispatcher.js +160 -0
- package/dist/adapters/bullmq/export.d.ts +31 -0
- package/dist/adapters/bullmq/export.js +53 -0
- package/dist/adapters/bullmq/extraction.d.ts +42 -0
- package/dist/adapters/bullmq/extraction.js +63 -0
- package/dist/adapters/bullmq/index.d.ts +13 -0
- package/dist/adapters/bullmq/index.js +13 -0
- package/dist/adapters/bullmq/lock.d.ts +77 -0
- package/dist/adapters/bullmq/lock.js +126 -0
- package/dist/adapters/bullmq/queue.d.ts +50 -0
- package/dist/adapters/bullmq/queue.js +81 -0
- package/dist/adapters/memory/artifact-exports.d.ts +11 -0
- package/dist/adapters/memory/artifact-exports.js +102 -0
- package/dist/adapters/memory/artifacts.d.ts +15 -0
- package/dist/adapters/memory/artifacts.js +134 -0
- package/dist/adapters/memory/blobs.d.ts +7 -0
- package/dist/adapters/memory/blobs.js +27 -0
- package/dist/adapters/memory/evaluation.d.ts +18 -0
- package/dist/adapters/memory/evaluation.js +148 -0
- package/dist/adapters/memory/files.d.ts +27 -0
- package/dist/adapters/memory/files.js +0 -0
- package/dist/adapters/memory/flows.d.ts +16 -0
- package/dist/adapters/memory/flows.js +117 -0
- package/dist/adapters/memory/hitl.d.ts +9 -0
- package/dist/adapters/memory/hitl.js +130 -0
- package/dist/adapters/memory/idempotency.d.ts +13 -0
- package/dist/adapters/memory/idempotency.js +32 -0
- package/dist/adapters/memory/index.d.ts +39 -0
- package/dist/adapters/memory/index.js +107 -0
- package/dist/adapters/memory/knowledge.d.ts +43 -0
- package/dist/adapters/memory/knowledge.js +248 -0
- package/dist/adapters/memory/mcp.d.ts +9 -0
- package/dist/adapters/memory/mcp.js +37 -0
- package/dist/adapters/memory/message-store.d.ts +17 -0
- package/dist/adapters/memory/message-store.js +70 -0
- package/dist/adapters/memory/principal-memory.d.ts +7 -0
- package/dist/adapters/memory/principal-memory.js +83 -0
- package/dist/adapters/memory/runtime.d.ts +29 -0
- package/dist/adapters/memory/runtime.js +0 -0
- package/dist/adapters/memory/sessions.d.ts +29 -0
- package/dist/adapters/memory/sessions.js +0 -0
- package/dist/adapters/memory/skills.d.ts +10 -0
- package/dist/adapters/memory/skills.js +41 -0
- package/dist/adapters/memory/thread-summary.d.ts +7 -0
- package/dist/adapters/memory/thread-summary.js +29 -0
- package/dist/adapters/memory/usage-limits.d.ts +13 -0
- package/dist/adapters/memory/usage-limits.js +72 -0
- package/dist/adapters/memory/usage.d.ts +16 -0
- package/dist/adapters/memory/usage.js +279 -0
- package/dist/adapters/otel/index.d.ts +111 -0
- package/dist/adapters/otel/index.js +133 -0
- package/dist/adapters/postgres/artifact-exports.d.ts +12 -0
- package/dist/adapters/postgres/artifact-exports.js +117 -0
- package/dist/adapters/postgres/artifacts.d.ts +16 -0
- package/dist/adapters/postgres/artifacts.js +172 -0
- package/dist/adapters/postgres/checkpoint-store.d.ts +16 -0
- package/dist/adapters/postgres/checkpoint-store.js +34 -0
- package/dist/adapters/postgres/config.d.ts +15 -0
- package/dist/adapters/postgres/config.js +187 -0
- package/dist/adapters/postgres/conversation-store.d.ts +4 -0
- package/dist/adapters/postgres/conversation-store.js +82 -0
- package/dist/adapters/postgres/evaluation.d.ts +17 -0
- package/dist/adapters/postgres/evaluation.js +193 -0
- package/dist/adapters/postgres/file-content.d.ts +30 -0
- package/dist/adapters/postgres/file-content.js +111 -0
- package/dist/adapters/postgres/files.d.ts +19 -0
- package/dist/adapters/postgres/files.js +209 -0
- package/dist/adapters/postgres/flows.d.ts +20 -0
- package/dist/adapters/postgres/flows.js +206 -0
- package/dist/adapters/postgres/hitl.d.ts +5 -0
- package/dist/adapters/postgres/hitl.js +247 -0
- package/dist/adapters/postgres/index.d.ts +35 -0
- package/dist/adapters/postgres/index.js +35 -0
- package/dist/adapters/postgres/knowledge.d.ts +48 -0
- package/dist/adapters/postgres/knowledge.js +255 -0
- package/dist/adapters/postgres/memory.d.ts +14 -0
- package/dist/adapters/postgres/memory.js +194 -0
- package/dist/adapters/postgres/message-store.d.ts +11 -0
- package/dist/adapters/postgres/message-store.js +145 -0
- package/dist/adapters/postgres/migrations.d.ts +69 -0
- package/dist/adapters/postgres/migrations.js +1594 -0
- package/dist/adapters/postgres/pg-executor.d.ts +19 -0
- package/dist/adapters/postgres/pg-executor.js +32 -0
- package/dist/adapters/postgres/retention.d.ts +26 -0
- package/dist/adapters/postgres/retention.js +59 -0
- package/dist/adapters/postgres/rollups.d.ts +17 -0
- package/dist/adapters/postgres/rollups.js +267 -0
- package/dist/adapters/postgres/run-coordinator.d.ts +5 -0
- package/dist/adapters/postgres/run-coordinator.js +98 -0
- package/dist/adapters/postgres/run-event-log.d.ts +26 -0
- package/dist/adapters/postgres/run-event-log.js +30 -0
- package/dist/adapters/postgres/run-store.d.ts +4 -0
- package/dist/adapters/postgres/run-store.js +199 -0
- package/dist/adapters/postgres/schema.d.ts +39 -0
- package/dist/adapters/postgres/schema.js +70 -0
- package/dist/adapters/postgres/session-state.d.ts +7 -0
- package/dist/adapters/postgres/session-state.js +99 -0
- package/dist/adapters/postgres/sql.d.ts +8 -0
- package/dist/adapters/postgres/sql.js +2 -0
- package/dist/adapters/postgres/transaction.d.ts +37 -0
- package/dist/adapters/postgres/transaction.js +93 -0
- package/dist/adapters/postgres/unit-of-work.d.ts +18 -0
- package/dist/adapters/postgres/unit-of-work.js +8 -0
- package/dist/adapters/postgres/usage-limits.d.ts +15 -0
- package/dist/adapters/postgres/usage-limits.js +136 -0
- package/dist/adapters/postgres/usage.d.ts +15 -0
- package/dist/adapters/postgres/usage.js +226 -0
- package/dist/adapters/redis/index.d.ts +9 -0
- package/dist/adapters/redis/index.js +9 -0
- package/dist/adapters/redis/realtime.d.ts +74 -0
- package/dist/adapters/redis/realtime.js +112 -0
- package/dist/adapters/supabase/index.d.ts +88 -0
- package/dist/adapters/supabase/index.js +84 -0
- package/dist/adapters/supabase/realtime.d.ts +30 -0
- package/dist/adapters/supabase/realtime.js +53 -0
- package/dist/adapters/supabase/rls.d.ts +99 -0
- package/dist/adapters/supabase/rls.js +216 -0
- package/dist/adapters/supabase/storage.d.ts +50 -0
- package/dist/adapters/supabase/storage.js +207 -0
- package/dist/agents/agent.d.ts +66 -0
- package/dist/agents/agent.js +209 -0
- package/dist/agents/define.d.ts +21 -0
- package/dist/agents/define.js +63 -0
- package/dist/agents/engine.d.ts +98 -0
- package/dist/agents/engine.js +462 -0
- package/dist/agents/index.d.ts +50 -0
- package/dist/agents/index.js +17 -0
- package/dist/artifacts/index.d.ts +114 -0
- package/dist/artifacts/index.js +219 -0
- package/dist/authorization/index.d.ts +76 -0
- package/dist/authorization/index.js +76 -0
- package/dist/capabilities/index.d.ts +120 -0
- package/dist/capabilities/index.js +167 -0
- package/dist/capabilities/runtime.d.ts +89 -0
- package/dist/capabilities/runtime.js +84 -0
- package/dist/citations/index.d.ts +161 -0
- package/dist/citations/index.js +182 -0
- package/dist/context/assembler.d.ts +82 -0
- package/dist/context/assembler.js +129 -0
- package/dist/context/compaction.d.ts +45 -0
- package/dist/context/compaction.js +55 -0
- package/dist/context/index.d.ts +75 -0
- package/dist/context/index.js +17 -0
- package/dist/core/content-parts.d.ts +194 -0
- package/dist/core/content-parts.js +23 -0
- package/dist/core/context.d.ts +51 -0
- package/dist/core/context.js +9 -0
- package/dist/core/env.d.ts +25 -0
- package/dist/core/env.js +41 -0
- package/dist/core/errors.d.ts +30 -0
- package/dist/core/errors.js +65 -0
- package/dist/core/events.d.ts +139 -0
- package/dist/core/events.js +99 -0
- package/dist/core/ids.d.ts +52 -0
- package/dist/core/ids.js +13 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +9 -0
- package/dist/core/tokens.d.ts +22 -0
- package/dist/core/tokens.js +22 -0
- package/dist/core/validation.d.ts +34 -0
- package/dist/core/validation.js +176 -0
- package/dist/documents/extraction.d.ts +121 -0
- package/dist/documents/extraction.js +293 -0
- package/dist/documents/index.d.ts +199 -0
- package/dist/documents/index.js +65 -0
- package/dist/documents/parsers/pdf.d.ts +47 -0
- package/dist/documents/parsers/pdf.js +508 -0
- package/dist/documents/parsers/text.d.ts +59 -0
- package/dist/documents/parsers/text.js +325 -0
- package/dist/documents/read-tool.d.ts +52 -0
- package/dist/documents/read-tool.js +109 -0
- package/dist/documents/render.d.ts +29 -0
- package/dist/documents/render.js +59 -0
- package/dist/documents/vision.d.ts +159 -0
- package/dist/documents/vision.js +214 -0
- package/dist/entries/adapters-bullmq.d.ts +8 -0
- package/dist/entries/adapters-bullmq.js +8 -0
- package/dist/entries/adapters-otel.d.ts +13 -0
- package/dist/entries/adapters-otel.js +13 -0
- package/dist/entries/adapters-postgres.d.ts +10 -0
- package/dist/entries/adapters-postgres.js +10 -0
- package/dist/entries/adapters-redis.d.ts +3 -0
- package/dist/entries/adapters-redis.js +3 -0
- package/dist/entries/context.d.ts +20 -0
- package/dist/entries/context.js +20 -0
- package/dist/entries/flows.d.ts +15 -0
- package/dist/entries/flows.js +15 -0
- package/dist/entries/hitl.d.ts +10 -0
- package/dist/entries/hitl.js +10 -0
- package/dist/entries/knowledge.d.ts +18 -0
- package/dist/entries/knowledge.js +19 -0
- package/dist/entries/mcp.d.ts +10 -0
- package/dist/entries/mcp.js +10 -0
- package/dist/entries/observability.d.ts +14 -0
- package/dist/entries/observability.js +16 -0
- package/dist/entries/persistence.d.ts +11 -0
- package/dist/entries/persistence.js +11 -0
- package/dist/entries/providers.d.ts +14 -0
- package/dist/entries/providers.js +14 -0
- package/dist/entries/runtime.d.ts +13 -0
- package/dist/entries/runtime.js +13 -0
- package/dist/entries/server.d.ts +24 -0
- package/dist/entries/server.js +24 -0
- package/dist/entries/tools.d.ts +21 -0
- package/dist/entries/tools.js +21 -0
- package/dist/entries/usage.d.ts +10 -0
- package/dist/entries/usage.js +10 -0
- package/dist/evaluation/gate.d.ts +168 -0
- package/dist/evaluation/gate.js +180 -0
- package/dist/evaluation/graders.d.ts +125 -0
- package/dist/evaluation/graders.js +203 -0
- package/dist/evaluation/index.d.ts +120 -0
- package/dist/evaluation/index.js +183 -0
- package/dist/evaluation/judge.d.ts +75 -0
- package/dist/evaluation/judge.js +111 -0
- package/dist/export/index.d.ts +162 -0
- package/dist/export/index.js +363 -0
- package/dist/export/markdown.d.ts +19 -0
- package/dist/export/markdown.js +29 -0
- package/dist/export/pdf.d.ts +73 -0
- package/dist/export/pdf.js +407 -0
- package/dist/files/context.d.ts +97 -0
- package/dist/files/context.js +185 -0
- package/dist/files/index.d.ts +210 -0
- package/dist/files/index.js +338 -0
- package/dist/files/read-tool.d.ts +81 -0
- package/dist/files/read-tool.js +163 -0
- package/dist/files/turn-parts.d.ts +96 -0
- package/dist/files/turn-parts.js +171 -0
- package/dist/flows/index.d.ts +270 -0
- package/dist/flows/index.js +62 -0
- package/dist/flows/interpreter.d.ts +146 -0
- package/dist/flows/interpreter.js +426 -0
- package/dist/flows/runner.d.ts +145 -0
- package/dist/flows/runner.js +270 -0
- package/dist/graphql/index.d.ts +8 -0
- package/dist/graphql/index.js +8 -0
- package/dist/graphql/resolvers.d.ts +237 -0
- package/dist/graphql/resolvers.js +253 -0
- package/dist/graphql/schema.d.ts +11 -0
- package/dist/graphql/schema.js +258 -0
- package/dist/graphql/sse.d.ts +77 -0
- package/dist/graphql/sse.js +100 -0
- package/dist/hitl/approved-execution.d.ts +127 -0
- package/dist/hitl/approved-execution.js +177 -0
- package/dist/hitl/index.d.ts +79 -0
- package/dist/hitl/index.js +12 -0
- package/dist/hitl/service.d.ts +221 -0
- package/dist/hitl/service.js +268 -0
- package/dist/idempotency/index.d.ts +70 -0
- package/dist/idempotency/index.js +59 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +60 -0
- package/dist/knowledge/chunking.d.ts +57 -0
- package/dist/knowledge/chunking.js +158 -0
- package/dist/knowledge/index.d.ts +119 -0
- package/dist/knowledge/index.js +166 -0
- package/dist/knowledge/retrieval.d.ts +146 -0
- package/dist/knowledge/retrieval.js +170 -0
- package/dist/loadtest/harness.d.ts +168 -0
- package/dist/loadtest/harness.js +507 -0
- package/dist/loadtest/index.d.ts +13 -0
- package/dist/loadtest/index.js +13 -0
- package/dist/loadtest/injection.d.ts +89 -0
- package/dist/loadtest/injection.js +147 -0
- package/dist/loadtest/metrics.d.ts +197 -0
- package/dist/loadtest/metrics.js +160 -0
- package/dist/loadtest/runbooks.d.ts +28 -0
- package/dist/loadtest/runbooks.js +159 -0
- package/dist/loadtest/scenario.d.ts +104 -0
- package/dist/loadtest/scenario.js +208 -0
- package/dist/mcp/egress.d.ts +53 -0
- package/dist/mcp/egress.js +115 -0
- package/dist/mcp/index.d.ts +93 -0
- package/dist/mcp/index.js +33 -0
- package/dist/mcp/provider.d.ts +62 -0
- package/dist/mcp/provider.js +0 -0
- package/dist/models/index.d.ts +98 -0
- package/dist/models/index.js +74 -0
- package/dist/models/pricing.d.ts +24 -0
- package/dist/models/pricing.js +37 -0
- package/dist/models/provider-factory.d.ts +31 -0
- package/dist/models/provider-factory.js +67 -0
- package/dist/models/streaming.d.ts +145 -0
- package/dist/models/streaming.js +272 -0
- package/dist/models/vision.d.ts +38 -0
- package/dist/models/vision.js +62 -0
- package/dist/persistence/index.d.ts +1654 -0
- package/dist/persistence/index.js +226 -0
- package/dist/principal-memory/index.d.ts +106 -0
- package/dist/principal-memory/index.js +89 -0
- package/dist/retention/index.d.ts +89 -0
- package/dist/retention/index.js +70 -0
- package/dist/runtime/checkpoint.d.ts +37 -0
- package/dist/runtime/checkpoint.js +22 -0
- package/dist/runtime/index.d.ts +118 -0
- package/dist/runtime/index.js +69 -0
- package/dist/runtime/retry.d.ts +95 -0
- package/dist/runtime/retry.js +126 -0
- package/dist/runtime/serialization.d.ts +85 -0
- package/dist/runtime/serialization.js +95 -0
- package/dist/runtime/streaming.d.ts +54 -0
- package/dist/runtime/streaming.js +115 -0
- package/dist/runtime/worker.d.ts +130 -0
- package/dist/runtime/worker.js +405 -0
- package/dist/security/checklist.d.ts +53 -0
- package/dist/security/checklist.js +204 -0
- package/dist/security/findings.d.ts +56 -0
- package/dist/security/findings.js +168 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/index.js +14 -0
- package/dist/security/prompt-safety.d.ts +100 -0
- package/dist/security/prompt-safety.js +133 -0
- package/dist/server/boot.d.ts +32 -0
- package/dist/server/boot.js +36 -0
- package/dist/server/cli-worker.d.ts +37 -0
- package/dist/server/cli-worker.js +151 -0
- package/dist/server/cli.d.ts +27 -0
- package/dist/server/cli.js +74 -0
- package/dist/server/config.d.ts +42 -0
- package/dist/server/config.js +127 -0
- package/dist/server/health.d.ts +59 -0
- package/dist/server/health.js +90 -0
- package/dist/server/host.d.ts +39 -0
- package/dist/server/host.js +124 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.js +15 -0
- package/dist/server/main.d.ts +16 -0
- package/dist/server/main.js +31 -0
- package/dist/server/sse-route.d.ts +21 -0
- package/dist/server/sse-route.js +282 -0
- package/dist/skills/index.d.ts +67 -0
- package/dist/skills/index.js +31 -0
- package/dist/skills/resolver.d.ts +54 -0
- package/dist/skills/resolver.js +121 -0
- package/dist/teams/index.d.ts +93 -0
- package/dist/teams/index.js +207 -0
- package/dist/telemetry/index.d.ts +157 -0
- package/dist/telemetry/index.js +71 -0
- package/dist/telemetry/instrument.d.ts +108 -0
- package/dist/telemetry/instrument.js +232 -0
- package/dist/telemetry/log-events.d.ts +17 -0
- package/dist/telemetry/log-events.js +58 -0
- package/dist/telemetry/metrics.d.ts +123 -0
- package/dist/telemetry/metrics.js +135 -0
- package/dist/telemetry/noop.d.ts +39 -0
- package/dist/telemetry/noop.js +143 -0
- package/dist/telemetry/redaction.d.ts +64 -0
- package/dist/telemetry/redaction.js +153 -0
- package/dist/telemetry/spans.d.ts +56 -0
- package/dist/telemetry/spans.js +78 -0
- package/dist/telemetry/trace-context.d.ts +55 -0
- package/dist/telemetry/trace-context.js +60 -0
- package/dist/toolkit/compute.d.ts +53 -0
- package/dist/toolkit/compute.js +152 -0
- package/dist/toolkit/data.d.ts +98 -0
- package/dist/toolkit/data.js +235 -0
- package/dist/toolkit/http.d.ts +113 -0
- package/dist/toolkit/http.js +205 -0
- package/dist/toolkit/index.d.ts +21 -0
- package/dist/toolkit/index.js +17 -0
- package/dist/toolkit/web.d.ts +107 -0
- package/dist/toolkit/web.js +147 -0
- package/dist/tools/define.d.ts +25 -0
- package/dist/tools/define.js +45 -0
- package/dist/tools/delegating.d.ts +132 -0
- package/dist/tools/delegating.js +211 -0
- package/dist/tools/index.d.ts +129 -0
- package/dist/tools/index.js +33 -0
- package/dist/tools/library/compute.d.ts +11 -0
- package/dist/tools/library/compute.js +46 -0
- package/dist/tools/library/data.d.ts +16 -0
- package/dist/tools/library/data.js +92 -0
- package/dist/tools/library/http.d.ts +28 -0
- package/dist/tools/library/http.js +71 -0
- package/dist/tools/library/index.d.ts +97 -0
- package/dist/tools/library/index.js +134 -0
- package/dist/tools/library/knowledge.d.ts +39 -0
- package/dist/tools/library/knowledge.js +58 -0
- package/dist/tools/library/web.d.ts +19 -0
- package/dist/tools/library/web.js +65 -0
- package/dist/tools/meta-tools.d.ts +19 -0
- package/dist/tools/meta-tools.js +36 -0
- package/dist/tools/registry.d.ts +146 -0
- package/dist/tools/registry.js +291 -0
- package/dist/usage/index.d.ts +105 -0
- package/dist/usage/index.js +20 -0
- package/dist/usage/quota.d.ts +258 -0
- package/dist/usage/quota.js +510 -0
- package/dist/usage/recorder.d.ts +29 -0
- package/dist/usage/recorder.js +96 -0
- package/dist/usage/rollups.d.ts +121 -0
- package/dist/usage/rollups.js +157 -0
- package/dist/worker/export.d.ts +57 -0
- package/dist/worker/export.js +81 -0
- package/dist/worker/extraction.d.ts +57 -0
- package/dist/worker/extraction.js +84 -0
- package/dist/worker/main.d.ts +103 -0
- package/dist/worker/main.js +159 -0
- package/package.json +187 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text, Markdown, CSV and JSON extraction (#131).
|
|
3
|
+
*
|
|
4
|
+
* The easy formats, and worth doing properly rather than passing straight through. A CSV handed to a model as
|
|
5
|
+
* one long string is the flattening this whole module exists to avoid — the row and column that give a number
|
|
6
|
+
* its meaning are exactly what gets lost. So a CSV becomes a `table` block with its cells intact, Markdown
|
|
7
|
+
* headings keep their level, and pipe tables survive as tables.
|
|
8
|
+
*
|
|
9
|
+
* Everything here is bounded the same way the PDF parser is, because "it's only text" is how a 200 MB CSV
|
|
10
|
+
* occupies a worker.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Accumulates blocks against the limits, so every parser stops the same way.
|
|
14
|
+
*
|
|
15
|
+
* Shared rather than repeated: the two parsers in this module and the PDF one all need the same three
|
|
16
|
+
* ceilings, and three copies of "am I over budget" is three chances for one of them to be wrong.
|
|
17
|
+
*/
|
|
18
|
+
export const createBlockBuilder = (limits) => {
|
|
19
|
+
const blocks = [];
|
|
20
|
+
const warnings = [];
|
|
21
|
+
let textBytes = 0;
|
|
22
|
+
let truncated = false;
|
|
23
|
+
const sizeOf = (block) => {
|
|
24
|
+
switch (block.kind) {
|
|
25
|
+
case "heading":
|
|
26
|
+
case "paragraph":
|
|
27
|
+
return block.text.length;
|
|
28
|
+
case "list":
|
|
29
|
+
return block.items.reduce((n, item) => n + item.length, 0);
|
|
30
|
+
case "table":
|
|
31
|
+
return block.rows.reduce((n, row) => n + row.reduce((m, cell) => m + cell.length, 0), 0);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
/** Returns false once full, so a caller can stop reading rather than keep parsing into a bin. */
|
|
36
|
+
push(block) {
|
|
37
|
+
if (truncated)
|
|
38
|
+
return false;
|
|
39
|
+
if (blocks.length >= limits.maxBlocks) {
|
|
40
|
+
truncated = true;
|
|
41
|
+
warnings.push(`Stopped after ${limits.maxBlocks} blocks; the document continues.`);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const size = sizeOf(block);
|
|
45
|
+
if (textBytes + size > limits.maxTextBytes) {
|
|
46
|
+
truncated = true;
|
|
47
|
+
warnings.push(`Stopped after ${limits.maxTextBytes} bytes of text; the document continues.`);
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
textBytes += size;
|
|
51
|
+
blocks.push(block);
|
|
52
|
+
return true;
|
|
53
|
+
},
|
|
54
|
+
warn(message) {
|
|
55
|
+
warnings.push(message);
|
|
56
|
+
},
|
|
57
|
+
get truncated() {
|
|
58
|
+
return truncated;
|
|
59
|
+
},
|
|
60
|
+
done(pageCount) {
|
|
61
|
+
return {
|
|
62
|
+
blocks,
|
|
63
|
+
...(pageCount === undefined ? {} : { pageCount }),
|
|
64
|
+
truncated,
|
|
65
|
+
warnings,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/** Collapse runs of whitespace. A PDF or a wrapped paragraph produces plenty, and none of it is meaning. */
|
|
71
|
+
const tidy = (text) => text.replace(/\s+/g, " ").trim();
|
|
72
|
+
const decode = (bytes) =>
|
|
73
|
+
// `fatal: false`: a byte sequence that is not valid UTF-8 should give a replacement character, not turn a
|
|
74
|
+
// readable document into an extraction failure.
|
|
75
|
+
new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
76
|
+
/**
|
|
77
|
+
* A pipe-table row split into cells.
|
|
78
|
+
*
|
|
79
|
+
* Returns null when the line is not a table row, so the caller's check and the split are one decision rather
|
|
80
|
+
* than a test followed by a parse that could disagree with it.
|
|
81
|
+
*/
|
|
82
|
+
const pipeCells = (line) => {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (!trimmed.includes("|"))
|
|
85
|
+
return null;
|
|
86
|
+
// Leading and trailing pipes are optional in Markdown, so they are stripped before splitting rather than
|
|
87
|
+
// producing phantom empty cells at both ends.
|
|
88
|
+
const inner = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
89
|
+
return inner.split("|").map((cell) => cell.trim());
|
|
90
|
+
};
|
|
91
|
+
/** `|---|:--:|` — the row that marks the line above as a header. Never content. */
|
|
92
|
+
const isPipeDivider = (line) => /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(line);
|
|
93
|
+
/**
|
|
94
|
+
* Markdown, and plain text as its degenerate case.
|
|
95
|
+
*
|
|
96
|
+
* Plain text has no headings or tables, so running it through the same parser costs nothing and means one
|
|
97
|
+
* code path instead of two that differ in how they split paragraphs.
|
|
98
|
+
*/
|
|
99
|
+
export const parseMarkdown = (bytes, limits) => {
|
|
100
|
+
const builder = createBlockBuilder(limits);
|
|
101
|
+
const lines = decode(bytes).split(/\r?\n/);
|
|
102
|
+
let paragraph = [];
|
|
103
|
+
const flushParagraph = () => {
|
|
104
|
+
if (paragraph.length === 0)
|
|
105
|
+
return true;
|
|
106
|
+
const text = tidy(paragraph.join(" "));
|
|
107
|
+
paragraph = [];
|
|
108
|
+
return text === "" ? true : builder.push({ kind: "paragraph", text });
|
|
109
|
+
};
|
|
110
|
+
let listItems = [];
|
|
111
|
+
let listOrdered = false;
|
|
112
|
+
const flushList = () => {
|
|
113
|
+
if (listItems.length === 0)
|
|
114
|
+
return true;
|
|
115
|
+
const items = listItems;
|
|
116
|
+
listItems = [];
|
|
117
|
+
return builder.push({ kind: "list", items, ordered: listOrdered });
|
|
118
|
+
};
|
|
119
|
+
let index = 0;
|
|
120
|
+
while (index < lines.length) {
|
|
121
|
+
const line = lines[index] ?? "";
|
|
122
|
+
const heading = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
123
|
+
if (heading) {
|
|
124
|
+
if (!flushParagraph() || !flushList())
|
|
125
|
+
break;
|
|
126
|
+
const level = heading[1]?.length ?? 1;
|
|
127
|
+
if (!builder.push({ kind: "heading", level: level, text: tidy(heading[2] ?? "") }))
|
|
128
|
+
break;
|
|
129
|
+
index += 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
// A table is recognised by its divider, not by containing a pipe: a sentence with a pipe in it is a
|
|
133
|
+
// sentence, and treating it as a one-column table would be worse than leaving it as prose.
|
|
134
|
+
const cells = pipeCells(line);
|
|
135
|
+
if (cells !== null && cells.length > 1 && isPipeDivider(lines[index + 1] ?? "")) {
|
|
136
|
+
if (!flushParagraph() || !flushList())
|
|
137
|
+
break;
|
|
138
|
+
const rows = [[...cells]];
|
|
139
|
+
let cursor = index + 2;
|
|
140
|
+
while (cursor < lines.length) {
|
|
141
|
+
const rowCells = pipeCells(lines[cursor] ?? "");
|
|
142
|
+
if (rowCells === null || rowCells.length <= 1)
|
|
143
|
+
break;
|
|
144
|
+
rows.push([...rowCells]);
|
|
145
|
+
cursor += 1;
|
|
146
|
+
}
|
|
147
|
+
if (!builder.push({ kind: "table", rows, hasHeader: true }))
|
|
148
|
+
break;
|
|
149
|
+
index = cursor;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const bullet = /^\s*(?:[-*+]|(\d+)[.)])\s+(.*)$/.exec(line);
|
|
153
|
+
if (bullet) {
|
|
154
|
+
if (!flushParagraph())
|
|
155
|
+
break;
|
|
156
|
+
const ordered = bullet[1] !== undefined;
|
|
157
|
+
// A bullet list running into a numbered one is two lists. Keeping them as one would renumber the
|
|
158
|
+
// second, which changes what the document said.
|
|
159
|
+
if (listItems.length > 0 && ordered !== listOrdered && !flushList())
|
|
160
|
+
break;
|
|
161
|
+
listOrdered = ordered;
|
|
162
|
+
listItems.push(tidy(bullet[2] ?? ""));
|
|
163
|
+
index += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (line.trim() === "") {
|
|
167
|
+
if (!flushParagraph() || !flushList())
|
|
168
|
+
break;
|
|
169
|
+
index += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (listItems.length > 0 && !flushList())
|
|
173
|
+
break;
|
|
174
|
+
paragraph.push(line);
|
|
175
|
+
index += 1;
|
|
176
|
+
}
|
|
177
|
+
flushParagraph();
|
|
178
|
+
flushList();
|
|
179
|
+
return builder.done();
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* A CSV row splitter that understands quotes.
|
|
183
|
+
*
|
|
184
|
+
* Not `line.split(",")`. A quoted field can contain a comma, a newline and an escaped quote, and a splitter
|
|
185
|
+
* that ignores that silently shifts every subsequent column — the worst possible failure for a table, because
|
|
186
|
+
* the result is plausible.
|
|
187
|
+
*/
|
|
188
|
+
export const parseDelimited = (bytes, limits, delimiter = ",") => {
|
|
189
|
+
const builder = createBlockBuilder(limits);
|
|
190
|
+
const text = decode(bytes);
|
|
191
|
+
const rows = [];
|
|
192
|
+
let row = [];
|
|
193
|
+
let field = "";
|
|
194
|
+
let quoted = false;
|
|
195
|
+
let index = 0;
|
|
196
|
+
const endField = () => {
|
|
197
|
+
row.push(field);
|
|
198
|
+
field = "";
|
|
199
|
+
};
|
|
200
|
+
const endRow = () => {
|
|
201
|
+
endField();
|
|
202
|
+
// A trailing newline would otherwise add a row of one empty cell to every file.
|
|
203
|
+
if (!(row.length === 1 && row[0] === ""))
|
|
204
|
+
rows.push(row);
|
|
205
|
+
row = [];
|
|
206
|
+
};
|
|
207
|
+
while (index < text.length) {
|
|
208
|
+
const char = text[index];
|
|
209
|
+
if (quoted) {
|
|
210
|
+
if (char === '"') {
|
|
211
|
+
// `""` inside a quoted field is one literal quote, not the end of the field.
|
|
212
|
+
if (text[index + 1] === '"') {
|
|
213
|
+
field += '"';
|
|
214
|
+
index += 2;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
quoted = false;
|
|
218
|
+
index += 1;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
field += char;
|
|
222
|
+
index += 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (char === '"' && field === "") {
|
|
226
|
+
quoted = true;
|
|
227
|
+
index += 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (char === delimiter) {
|
|
231
|
+
endField();
|
|
232
|
+
index += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (char === "\n") {
|
|
236
|
+
endRow();
|
|
237
|
+
index += 1;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (char === "\r") {
|
|
241
|
+
index += 1;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
field += char;
|
|
245
|
+
index += 1;
|
|
246
|
+
}
|
|
247
|
+
if (field !== "" || row.length > 0)
|
|
248
|
+
endRow();
|
|
249
|
+
if (rows.length === 0)
|
|
250
|
+
return builder.done();
|
|
251
|
+
// The first row is the header when every cell is non-empty — the usual case, and a header of blanks is a
|
|
252
|
+
// sign the file has none rather than a header worth claiming.
|
|
253
|
+
const hasHeader = (rows[0] ?? []).every((cell) => cell.trim() !== "");
|
|
254
|
+
builder.push({ kind: "table", rows, hasHeader });
|
|
255
|
+
return builder.done();
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* JSON as a document.
|
|
259
|
+
*
|
|
260
|
+
* An array of flat objects is a table and is by far the most common shape a JSON attachment has, so it is
|
|
261
|
+
* extracted as one. Anything else is rendered as indented text: still readable, and honest about the fact
|
|
262
|
+
* that there was no table to find.
|
|
263
|
+
*/
|
|
264
|
+
export const parseJsonDocument = (bytes, limits) => {
|
|
265
|
+
const builder = createBlockBuilder(limits);
|
|
266
|
+
let parsed;
|
|
267
|
+
try {
|
|
268
|
+
parsed = JSON.parse(decode(bytes));
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return { reason: "malformed", message: "That file is not valid JSON." };
|
|
272
|
+
}
|
|
273
|
+
const isFlatRecord = (value) => typeof value === "object" &&
|
|
274
|
+
value !== null &&
|
|
275
|
+
!Array.isArray(value) &&
|
|
276
|
+
Object.values(value).every((v) => v === null || ["string", "number", "boolean"].includes(typeof v));
|
|
277
|
+
if (Array.isArray(parsed) && parsed.length > 0 && parsed.every(isFlatRecord)) {
|
|
278
|
+
// The union of keys, in first-seen order: a record missing a key gets an empty cell rather than shifting
|
|
279
|
+
// the row, which is the same silent-corruption failure quoted CSV fields avoid.
|
|
280
|
+
const columns = [];
|
|
281
|
+
for (const record of parsed)
|
|
282
|
+
for (const key of Object.keys(record))
|
|
283
|
+
if (!columns.includes(key))
|
|
284
|
+
columns.push(key);
|
|
285
|
+
const rows = [columns, ...parsed.map((record) => columns.map((c) => String(record[c] ?? "")))];
|
|
286
|
+
builder.push({ kind: "table", rows, hasHeader: true });
|
|
287
|
+
return builder.done();
|
|
288
|
+
}
|
|
289
|
+
builder.push({ kind: "paragraph", text: tidy(JSON.stringify(parsed, null, 2)) });
|
|
290
|
+
return builder.done();
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* The parser for every text-shaped type.
|
|
294
|
+
*
|
|
295
|
+
* One parser dispatching on the type it was handed, rather than five parsers sharing a builder. The formats
|
|
296
|
+
* differ only in how a line is read; the bounds, the decoding and the block shapes are identical, and five
|
|
297
|
+
* copies of those is five chances for one to be wrong.
|
|
298
|
+
*/
|
|
299
|
+
export const TEXT_MEDIA_TYPES = [
|
|
300
|
+
"text/plain",
|
|
301
|
+
"text/markdown",
|
|
302
|
+
"text/x-markdown",
|
|
303
|
+
"text/csv",
|
|
304
|
+
"text/tab-separated-values",
|
|
305
|
+
"application/json",
|
|
306
|
+
];
|
|
307
|
+
export const createTextDocumentParser = () => ({
|
|
308
|
+
id: "text",
|
|
309
|
+
mediaTypes: TEXT_MEDIA_TYPES,
|
|
310
|
+
async parse({ bytes, mediaType, limits }) {
|
|
311
|
+
switch (mediaType) {
|
|
312
|
+
case "text/csv":
|
|
313
|
+
return parseDelimited(bytes, limits, ",");
|
|
314
|
+
case "text/tab-separated-values":
|
|
315
|
+
return parseDelimited(bytes, limits, "\t");
|
|
316
|
+
case "application/json":
|
|
317
|
+
return parseJsonDocument(bytes, limits);
|
|
318
|
+
default:
|
|
319
|
+
// Plain text goes through the Markdown parser: plain text is Markdown with no markup, so one path
|
|
320
|
+
// handles both and a `.txt` file that happens to use `#` headings is read the way its author meant.
|
|
321
|
+
return parseMarkdown(bytes, limits);
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `read_document` — bounded reads of extracted structure (#131, AC-6).
|
|
3
|
+
*
|
|
4
|
+
* The counterpart to #130's `read_attachment`, and the reason both exist rather than one: `read_attachment`
|
|
5
|
+
* returns raw bytes, which for a PDF is binary noise. This returns the *extracted* document, rendered as
|
|
6
|
+
* Markdown so a table is still a table, in a bounded window.
|
|
7
|
+
*
|
|
8
|
+
* **The window is in blocks, not characters.** A character bound can land inside a table and hand the model
|
|
9
|
+
* half of one, which is worse than no table because the missing rows are invisible — the model answers
|
|
10
|
+
* confidently from the half it can see. A block is the smallest unit that is still true on its own.
|
|
11
|
+
*
|
|
12
|
+
* When there is nothing to read, this reports *why* (AC-4). "That PDF is a scan and needs OCR" is a sentence
|
|
13
|
+
* the assistant can pass on; an empty result is one it would paper over.
|
|
14
|
+
*/
|
|
15
|
+
import type { Tool } from "../tools/index.js";
|
|
16
|
+
import type { ExtractionService } from "./extraction.js";
|
|
17
|
+
import { summariseBlocks } from "./render.js";
|
|
18
|
+
/**
|
|
19
|
+
* Blocks per call, and the character ceiling that overrides it.
|
|
20
|
+
*
|
|
21
|
+
* Both, because either alone is escapable: fifty paragraphs is a reasonable window and fifty *tables* is not,
|
|
22
|
+
* so the block count bounds the common case and the character count bounds the adversarial one.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MAX_BLOCKS_PER_READ = 50;
|
|
25
|
+
export declare const MAX_CHARS_PER_READ = 24000;
|
|
26
|
+
export type ReadDocumentOutput = {
|
|
27
|
+
readonly fileId: string;
|
|
28
|
+
readonly text: string;
|
|
29
|
+
readonly fromBlock: number;
|
|
30
|
+
readonly blocksReturned: number;
|
|
31
|
+
readonly totalBlocks: number;
|
|
32
|
+
readonly truncated: boolean;
|
|
33
|
+
readonly nextBlock?: number;
|
|
34
|
+
/** Present when extraction itself truncated the document — a different fact from this window truncating. */
|
|
35
|
+
readonly documentTruncated?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* OCR/vision confidence, 0–1, and only when the extraction was probabilistic (#132).
|
|
38
|
+
*
|
|
39
|
+
* Present alongside the low-confidence warning rather than instead of it: the warning is the sentence a
|
|
40
|
+
* model should pass on, and the number is what a caller comparing two extractions needs.
|
|
41
|
+
*/
|
|
42
|
+
readonly confidence?: number;
|
|
43
|
+
readonly lowConfidence?: boolean;
|
|
44
|
+
readonly warnings?: readonly string[];
|
|
45
|
+
};
|
|
46
|
+
export declare const createReadDocumentTool: (deps: {
|
|
47
|
+
readonly extraction: ExtractionService;
|
|
48
|
+
}) => Tool;
|
|
49
|
+
/** A one-line description of a document's shape, for a context section that must stay cheap. */
|
|
50
|
+
export declare const describeExtraction: (blockCount: number, pageCount: number | undefined) => string;
|
|
51
|
+
export { summariseBlocks };
|
|
52
|
+
//# sourceMappingURL=read-tool.d.ts.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `read_document` — bounded reads of extracted structure (#131, AC-6).
|
|
3
|
+
*
|
|
4
|
+
* The counterpart to #130's `read_attachment`, and the reason both exist rather than one: `read_attachment`
|
|
5
|
+
* returns raw bytes, which for a PDF is binary noise. This returns the *extracted* document, rendered as
|
|
6
|
+
* Markdown so a table is still a table, in a bounded window.
|
|
7
|
+
*
|
|
8
|
+
* **The window is in blocks, not characters.** A character bound can land inside a table and hand the model
|
|
9
|
+
* half of one, which is worse than no table because the missing rows are invisible — the model answers
|
|
10
|
+
* confidently from the half it can see. A block is the smallest unit that is still true on its own.
|
|
11
|
+
*
|
|
12
|
+
* When there is nothing to read, this reports *why* (AC-4). "That PDF is a scan and needs OCR" is a sentence
|
|
13
|
+
* the assistant can pass on; an empty result is one it would paper over.
|
|
14
|
+
*/
|
|
15
|
+
import { AgentPlatformError } from "../core/errors.js";
|
|
16
|
+
import { asId } from "../core/ids.js";
|
|
17
|
+
import { defineTool } from "../tools/define.js";
|
|
18
|
+
import { renderBlocks, summariseBlocks } from "./render.js";
|
|
19
|
+
import { LOW_CONFIDENCE_THRESHOLD } from "../persistence/index.js";
|
|
20
|
+
/**
|
|
21
|
+
* Blocks per call, and the character ceiling that overrides it.
|
|
22
|
+
*
|
|
23
|
+
* Both, because either alone is escapable: fifty paragraphs is a reasonable window and fifty *tables* is not,
|
|
24
|
+
* so the block count bounds the common case and the character count bounds the adversarial one.
|
|
25
|
+
*/
|
|
26
|
+
export const MAX_BLOCKS_PER_READ = 50;
|
|
27
|
+
export const MAX_CHARS_PER_READ = 24_000;
|
|
28
|
+
export const createReadDocumentTool = (deps) => defineTool({
|
|
29
|
+
name: "read_document",
|
|
30
|
+
label: "Read document",
|
|
31
|
+
description: `Read a bounded portion of an attached document's extracted text, with headings and tables preserved. ` +
|
|
32
|
+
`At most ${MAX_BLOCKS_PER_READ} blocks per call; continue from the block index it returns.`,
|
|
33
|
+
category: "files",
|
|
34
|
+
effect: "read",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object",
|
|
37
|
+
required: ["fileId"],
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
properties: {
|
|
40
|
+
fileId: { type: "string", description: "The id from the attachment reference, without the `file:` prefix." },
|
|
41
|
+
fromBlock: { type: "integer", minimum: 0, description: "Block index to start from. Defaults to 0." },
|
|
42
|
+
maxBlocks: {
|
|
43
|
+
type: "integer",
|
|
44
|
+
minimum: 1,
|
|
45
|
+
maximum: MAX_BLOCKS_PER_READ,
|
|
46
|
+
description: `At most ${MAX_BLOCKS_PER_READ}. A larger value is clamped, not refused.`,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
async execute(input, context) {
|
|
51
|
+
const id = asId(input.fileId.replace(/^file:/, ""));
|
|
52
|
+
const { document, extraction } = await deps.extraction.getExtracted(context, id);
|
|
53
|
+
if (document === null) {
|
|
54
|
+
// The reason, not an empty result. This is AC-4 reaching the user: the message a parser wrote is the
|
|
55
|
+
// message the assistant repeats, so "that PDF is a scan" survives all the way out.
|
|
56
|
+
const detail = extraction === undefined
|
|
57
|
+
? "That file has not been processed for text yet. Try again shortly."
|
|
58
|
+
: (extraction.failureMessage ??
|
|
59
|
+
(extraction.state === "pending" || extraction.state === "running"
|
|
60
|
+
? "That document is still being processed. Try again shortly."
|
|
61
|
+
: "No extracted text is available for that document."));
|
|
62
|
+
throw new AgentPlatformError({ code: "not_found", message: detail, retryable: extraction?.state !== "failed" });
|
|
63
|
+
}
|
|
64
|
+
const from = Math.max(0, Math.floor(input.fromBlock ?? 0));
|
|
65
|
+
// Clamped rather than refused: a model asking for more gets less, which it can act on, instead of an
|
|
66
|
+
// error it would answer with a retry.
|
|
67
|
+
const wanted = Math.min(Math.max(1, Math.floor(input.maxBlocks ?? MAX_BLOCKS_PER_READ)), MAX_BLOCKS_PER_READ);
|
|
68
|
+
const taken = [];
|
|
69
|
+
let chars = 0;
|
|
70
|
+
for (const block of document.blocks.slice(from, from + wanted)) {
|
|
71
|
+
const rendered = renderBlocks([block]);
|
|
72
|
+
// The character ceiling stops *before* adding an oversized block rather than after, but always takes
|
|
73
|
+
// at least one: a single table larger than the ceiling would otherwise return nothing at all, and a
|
|
74
|
+
// window that can return nothing is a window a model cannot page past.
|
|
75
|
+
if (chars + rendered.length > MAX_CHARS_PER_READ && taken.length > 0)
|
|
76
|
+
break;
|
|
77
|
+
taken.push(block);
|
|
78
|
+
chars += rendered.length;
|
|
79
|
+
}
|
|
80
|
+
const end = from + taken.length;
|
|
81
|
+
const truncated = end < document.blocks.length;
|
|
82
|
+
return {
|
|
83
|
+
fileId: id,
|
|
84
|
+
text: renderBlocks(taken),
|
|
85
|
+
fromBlock: from,
|
|
86
|
+
blocksReturned: taken.length,
|
|
87
|
+
totalBlocks: document.blocks.length,
|
|
88
|
+
truncated,
|
|
89
|
+
...(truncated ? { nextBlock: end } : {}),
|
|
90
|
+
// Only when true. A `false` on every response is a field a model has to read and discard.
|
|
91
|
+
...(document.truncated ? { documentTruncated: true } : {}),
|
|
92
|
+
// AC-5 reaching the model. The flag is derived here as well as warned about in the document, so a
|
|
93
|
+
// consumer that only reads structured fields still cannot mistake uncertain text for certain.
|
|
94
|
+
...(document.confidence === undefined
|
|
95
|
+
? {}
|
|
96
|
+
: {
|
|
97
|
+
confidence: document.confidence,
|
|
98
|
+
...(document.confidence < LOW_CONFIDENCE_THRESHOLD ? { lowConfidence: true } : {}),
|
|
99
|
+
}),
|
|
100
|
+
...(document.warnings.length > 0 ? { warnings: document.warnings } : {}),
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
/** A one-line description of a document's shape, for a context section that must stay cheap. */
|
|
105
|
+
export const describeExtraction = (blockCount, pageCount) => pageCount !== undefined && pageCount > 0
|
|
106
|
+
? `${pageCount} page${pageCount === 1 ? "" : "s"}, ${blockCount} block${blockCount === 1 ? "" : "s"}`
|
|
107
|
+
: `${blockCount} block${blockCount === 1 ? "" : "s"}`;
|
|
108
|
+
export { summariseBlocks };
|
|
109
|
+
//# sourceMappingURL=read-tool.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering extracted structure back to text for a model (#131).
|
|
3
|
+
*
|
|
4
|
+
* The counterpart to extraction, and the reason extracting to blocks rather than a string pays off: a table
|
|
5
|
+
* is rendered as a Markdown table, so the model sees the same rows and columns the document had. Flattening
|
|
6
|
+
* happened nowhere — not at extraction, and not here.
|
|
7
|
+
*
|
|
8
|
+
* Markdown specifically, because it is the format models are most reliably trained on for tabular text and
|
|
9
|
+
* the one where a cell's column is unambiguous. A CSV rendering would be more compact and would lose the
|
|
10
|
+
* header's distinctness; an ASCII-art table would cost far more tokens for no gain.
|
|
11
|
+
*/
|
|
12
|
+
import type { DocumentBlock } from "./index.js";
|
|
13
|
+
export declare const renderBlock: (block: DocumentBlock) => string;
|
|
14
|
+
/**
|
|
15
|
+
* A window of blocks, rendered.
|
|
16
|
+
*
|
|
17
|
+
* Blocks rather than characters, because a bound that can land inside a table would hand the model half a
|
|
18
|
+
* table — which is worse than no table, since the missing rows are invisible. A block is the smallest unit
|
|
19
|
+
* that is still meaningful on its own.
|
|
20
|
+
*/
|
|
21
|
+
export declare const renderBlocks: (blocks: readonly DocumentBlock[]) => string;
|
|
22
|
+
/**
|
|
23
|
+
* A one-line description of what a document contains.
|
|
24
|
+
*
|
|
25
|
+
* For the context section: enough for a model to decide whether reading it is worth the budget, at a cost
|
|
26
|
+
* that does not depend on the document's length.
|
|
27
|
+
*/
|
|
28
|
+
export declare const summariseBlocks: (blocks: readonly DocumentBlock[]) => string;
|
|
29
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering extracted structure back to text for a model (#131).
|
|
3
|
+
*
|
|
4
|
+
* The counterpart to extraction, and the reason extracting to blocks rather than a string pays off: a table
|
|
5
|
+
* is rendered as a Markdown table, so the model sees the same rows and columns the document had. Flattening
|
|
6
|
+
* happened nowhere — not at extraction, and not here.
|
|
7
|
+
*
|
|
8
|
+
* Markdown specifically, because it is the format models are most reliably trained on for tabular text and
|
|
9
|
+
* the one where a cell's column is unambiguous. A CSV rendering would be more compact and would lose the
|
|
10
|
+
* header's distinctness; an ASCII-art table would cost far more tokens for no gain.
|
|
11
|
+
*/
|
|
12
|
+
/** A pipe cell cannot contain a raw `|` without ending the cell, and a newline would end the row. */
|
|
13
|
+
const cell = (text) => text.replace(/\|/g, "\\|").replace(/\s*\n\s*/g, " ");
|
|
14
|
+
export const renderBlock = (block) => {
|
|
15
|
+
switch (block.kind) {
|
|
16
|
+
case "heading":
|
|
17
|
+
return `${"#".repeat(block.level)} ${block.text}`;
|
|
18
|
+
case "paragraph":
|
|
19
|
+
return block.text;
|
|
20
|
+
case "list":
|
|
21
|
+
return block.items.map((item, i) => (block.ordered ? `${i + 1}. ${item}` : `- ${item}`)).join("\n");
|
|
22
|
+
case "table": {
|
|
23
|
+
const rows = block.rows.map((row) => `| ${row.map(cell).join(" | ")} |`);
|
|
24
|
+
if (!block.hasHeader || rows.length === 0)
|
|
25
|
+
return rows.join("\n");
|
|
26
|
+
// The divider is what makes the first row a header rather than just the first row. Its width has to
|
|
27
|
+
// match the header's cell count or the table reads as malformed.
|
|
28
|
+
const divider = `| ${(block.rows[0] ?? []).map(() => "---").join(" | ")} |`;
|
|
29
|
+
return [rows[0], divider, ...rows.slice(1)].join("\n");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* A window of blocks, rendered.
|
|
35
|
+
*
|
|
36
|
+
* Blocks rather than characters, because a bound that can land inside a table would hand the model half a
|
|
37
|
+
* table — which is worse than no table, since the missing rows are invisible. A block is the smallest unit
|
|
38
|
+
* that is still meaningful on its own.
|
|
39
|
+
*/
|
|
40
|
+
export const renderBlocks = (blocks) => blocks.map(renderBlock).join("\n\n");
|
|
41
|
+
/**
|
|
42
|
+
* A one-line description of what a document contains.
|
|
43
|
+
*
|
|
44
|
+
* For the context section: enough for a model to decide whether reading it is worth the budget, at a cost
|
|
45
|
+
* that does not depend on the document's length.
|
|
46
|
+
*/
|
|
47
|
+
export const summariseBlocks = (blocks) => {
|
|
48
|
+
const counts = { heading: 0, paragraph: 0, table: 0, list: 0 };
|
|
49
|
+
for (const block of blocks)
|
|
50
|
+
counts[block.kind] += 1;
|
|
51
|
+
const parts = [
|
|
52
|
+
counts.heading > 0 ? `${counts.heading} heading${counts.heading === 1 ? "" : "s"}` : null,
|
|
53
|
+
counts.paragraph > 0 ? `${counts.paragraph} paragraph${counts.paragraph === 1 ? "" : "s"}` : null,
|
|
54
|
+
counts.table > 0 ? `${counts.table} table${counts.table === 1 ? "" : "s"}` : null,
|
|
55
|
+
counts.list > 0 ? `${counts.list} list${counts.list === 1 ? "" : "s"}` : null,
|
|
56
|
+
].filter((part) => part !== null);
|
|
57
|
+
return parts.length === 0 ? "no content" : parts.join(", ");
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=render.js.map
|