veryfront 0.1.623 → 0.1.625

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 (151) hide show
  1. package/README.md +3 -3
  2. package/esm/cli/commands/knowledge/command-help.d.ts.map +1 -1
  3. package/esm/cli/commands/knowledge/command-help.js +2 -3
  4. package/esm/cli/commands/knowledge/command.d.ts +2 -47
  5. package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
  6. package/esm/cli/commands/knowledge/command.js +22 -124
  7. package/esm/cli/commands/knowledge/parser.d.ts +40 -0
  8. package/esm/cli/commands/knowledge/parser.d.ts.map +1 -0
  9. package/esm/cli/commands/knowledge/parser.js +353 -0
  10. package/esm/cli/commands/knowledge/result.d.ts +3 -3
  11. package/esm/cli/commands/knowledge/result.js +1 -1
  12. package/esm/cli/commands/styles/command.js +2 -2
  13. package/esm/cli/commands/task/command.js +4 -4
  14. package/esm/cli/commands/worker/command-help.js +4 -4
  15. package/esm/cli/commands/worker/command.d.ts +1 -1
  16. package/esm/cli/commands/worker/command.js +9 -9
  17. package/esm/cli/commands/workflow/command.js +4 -4
  18. package/esm/cli/utils/sanitize-run-output.d.ts +2 -0
  19. package/esm/cli/utils/sanitize-run-output.d.ts.map +1 -0
  20. package/esm/cli/utils/{sanitize-job-output.js → sanitize-run-output.js} +1 -1
  21. package/esm/cli/utils/write-run-result.d.ts +2 -0
  22. package/esm/cli/utils/write-run-result.d.ts.map +1 -0
  23. package/esm/cli/utils/write-run-result.js +16 -0
  24. package/esm/deno.d.ts +4 -3
  25. package/esm/deno.js +6 -5
  26. package/esm/src/build/production-build/static-generation.d.ts.map +1 -1
  27. package/esm/src/build/production-build/static-generation.js +85 -0
  28. package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
  29. package/esm/src/html/hydration-script-builder/templates/renderer.js +37 -4
  30. package/esm/src/integrations/_data.js +1 -1
  31. package/esm/src/integrations/schema.d.ts +8 -0
  32. package/esm/src/integrations/schema.d.ts.map +1 -1
  33. package/esm/src/integrations/schema.js +3 -0
  34. package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
  35. package/esm/src/internal-agents/run-stream.js +20 -3
  36. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +2 -2
  37. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
  38. package/esm/src/platform/adapters/veryfront-api-client/operations.js +2 -2
  39. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +1 -1
  40. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.js +1 -1
  41. package/esm/src/react/components/chat/chat/components/attachment-pill.js +1 -1
  42. package/esm/src/react/components/chat/chat/composition/chat-composer.d.ts +1 -0
  43. package/esm/src/react/components/chat/chat/composition/chat-composer.d.ts.map +1 -1
  44. package/esm/src/react/components/chat/chat/composition/chat-composer.js +27 -11
  45. package/esm/src/react/components/chat/chat/index.d.ts +1 -0
  46. package/esm/src/react/components/chat/chat/index.d.ts.map +1 -1
  47. package/esm/src/react/components/chat/chat/index.js +2 -2
  48. package/esm/src/react/components/chat/theme.js +1 -1
  49. package/esm/src/rendering/layouts/utils/applicator.d.ts.map +1 -1
  50. package/esm/src/rendering/layouts/utils/applicator.js +1 -1
  51. package/esm/src/rendering/layouts/utils/component-loader.d.ts +8 -0
  52. package/esm/src/rendering/layouts/utils/component-loader.d.ts.map +1 -1
  53. package/esm/src/rendering/layouts/utils/component-loader.js +19 -0
  54. package/esm/src/rendering/rsc/client-boot.ts +23 -6
  55. package/esm/src/runs/index.d.ts +27 -0
  56. package/esm/src/runs/index.d.ts.map +1 -0
  57. package/esm/src/runs/index.js +26 -0
  58. package/esm/src/runs/runs-client.d.ts +95 -0
  59. package/esm/src/runs/runs-client.d.ts.map +1 -0
  60. package/esm/src/runs/runs-client.js +181 -0
  61. package/esm/src/{jobs → runs}/runtime-env.d.ts.map +1 -1
  62. package/esm/src/{jobs → runs}/runtime-env.js +2 -2
  63. package/esm/src/runs/schemas.d.ts +236 -0
  64. package/esm/src/runs/schemas.d.ts.map +1 -0
  65. package/esm/src/runs/schemas.js +61 -0
  66. package/esm/src/security/deno-permissions.d.ts +2 -2
  67. package/esm/src/security/deno-permissions.js +2 -2
  68. package/esm/src/security/index.d.ts +1 -1
  69. package/esm/src/security/index.js +1 -1
  70. package/esm/src/server/build-app-route-renderer.d.ts +2 -0
  71. package/esm/src/server/build-app-route-renderer.d.ts.map +1 -1
  72. package/esm/src/server/build-app-route-renderer.js +51 -28
  73. package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
  74. package/esm/src/server/handlers/dev/framework-candidates.generated.js +54 -8
  75. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.d.ts.map +1 -1
  76. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +1 -1
  77. package/esm/src/server/services/static/static-file.service.d.ts.map +1 -1
  78. package/esm/src/server/services/static/static-file.service.js +9 -4
  79. package/esm/src/skill/index.d.ts +1 -1
  80. package/esm/src/skill/index.d.ts.map +1 -1
  81. package/esm/src/skill/index.js +1 -1
  82. package/esm/src/skill/prompt-augmentation.js +2 -2
  83. package/esm/src/skill/tools.d.ts +1 -1
  84. package/esm/src/skill/tools.d.ts.map +1 -1
  85. package/esm/src/skill/tools.js +11 -9
  86. package/esm/src/skill/types.d.ts +1 -0
  87. package/esm/src/skill/types.d.ts.map +1 -1
  88. package/esm/src/skill/types.js +1 -0
  89. package/esm/src/task/runner.js +1 -1
  90. package/esm/src/task/types.d.ts +4 -4
  91. package/esm/src/task/types.d.ts.map +1 -1
  92. package/esm/src/task/types.js +1 -1
  93. package/esm/src/tool/factory.d.ts.map +1 -1
  94. package/esm/src/tool/factory.js +10 -0
  95. package/esm/src/tool/types.d.ts +5 -4
  96. package/esm/src/tool/types.d.ts.map +1 -1
  97. package/esm/src/utils/index.d.ts +1 -1
  98. package/esm/src/utils/index.js +1 -1
  99. package/esm/src/utils/logger/index.d.ts +1 -1
  100. package/esm/src/utils/logger/index.js +1 -1
  101. package/esm/src/utils/logger/logger.d.ts +6 -6
  102. package/esm/src/utils/logger/logger.d.ts.map +1 -1
  103. package/esm/src/utils/logger/logger.js +10 -10
  104. package/esm/src/utils/version-constant.d.ts +1 -1
  105. package/esm/src/utils/version-constant.js +1 -1
  106. package/esm/src/workflow/executor/workflow-executor.js +3 -3
  107. package/esm/src/workflow/worker/{dynamic-job-entrypoint.d.ts → dynamic-run-entrypoint.d.ts} +2 -2
  108. package/esm/src/workflow/worker/{dynamic-job-entrypoint.d.ts.map → dynamic-run-entrypoint.d.ts.map} +1 -1
  109. package/esm/src/workflow/worker/{dynamic-job-entrypoint.js → dynamic-run-entrypoint.js} +1 -1
  110. package/esm/src/workflow/worker/executors/index.d.ts +8 -8
  111. package/esm/src/workflow/worker/executors/index.d.ts.map +1 -1
  112. package/esm/src/workflow/worker/executors/index.js +5 -5
  113. package/esm/src/workflow/worker/executors/k8s.d.ts +34 -34
  114. package/esm/src/workflow/worker/executors/k8s.d.ts.map +1 -1
  115. package/esm/src/workflow/worker/executors/k8s.js +59 -58
  116. package/esm/src/workflow/worker/executors/process.d.ts +15 -15
  117. package/esm/src/workflow/worker/executors/process.d.ts.map +1 -1
  118. package/esm/src/workflow/worker/executors/process.js +86 -86
  119. package/esm/src/workflow/worker/executors/types.d.ts +38 -39
  120. package/esm/src/workflow/worker/executors/types.d.ts.map +1 -1
  121. package/esm/src/workflow/worker/executors/types.js +9 -10
  122. package/esm/src/workflow/worker/index.d.ts +7 -7
  123. package/esm/src/workflow/worker/index.d.ts.map +1 -1
  124. package/esm/src/workflow/worker/index.js +9 -9
  125. package/esm/src/workflow/worker/{job-entrypoint.d.ts → run-entrypoint.d.ts} +3 -3
  126. package/esm/src/workflow/worker/{job-entrypoint.d.ts.map → run-entrypoint.d.ts.map} +1 -1
  127. package/esm/src/workflow/worker/{job-entrypoint.js → run-entrypoint.js} +2 -2
  128. package/esm/src/workflow/worker/{job-manager.d.ts → run-manager.d.ts} +32 -32
  129. package/esm/src/workflow/worker/run-manager.d.ts.map +1 -0
  130. package/esm/src/workflow/worker/{job-manager.js → run-manager.js} +63 -63
  131. package/esm/src/workflow/worker/shared.js +1 -1
  132. package/package.json +4 -4
  133. package/esm/cli/commands/knowledge/parser-source.d.ts +0 -2
  134. package/esm/cli/commands/knowledge/parser-source.d.ts.map +0 -1
  135. package/esm/cli/commands/knowledge/parser-source.js +0 -641
  136. package/esm/cli/utils/sanitize-job-output.d.ts +0 -2
  137. package/esm/cli/utils/sanitize-job-output.d.ts.map +0 -1
  138. package/esm/cli/utils/write-job-result.d.ts +0 -2
  139. package/esm/cli/utils/write-job-result.d.ts.map +0 -1
  140. package/esm/cli/utils/write-job-result.js +0 -16
  141. package/esm/src/jobs/index.d.ts +0 -25
  142. package/esm/src/jobs/index.d.ts.map +0 -1
  143. package/esm/src/jobs/index.js +0 -24
  144. package/esm/src/jobs/jobs-client.d.ts +0 -182
  145. package/esm/src/jobs/jobs-client.d.ts.map +0 -1
  146. package/esm/src/jobs/jobs-client.js +0 -255
  147. package/esm/src/jobs/schemas.d.ts +0 -1146
  148. package/esm/src/jobs/schemas.d.ts.map +0 -1
  149. package/esm/src/jobs/schemas.js +0 -294
  150. package/esm/src/workflow/worker/job-manager.d.ts.map +0 -1
  151. /package/esm/src/{jobs → runs}/runtime-env.d.ts +0 -0
@@ -1,641 +0,0 @@
1
- export const knowledgeIngestPythonSource = String.raw `#!/usr/bin/env python3
2
- import argparse
3
- import csv
4
- import json
5
- import os
6
- import re
7
- import subprocess
8
- import tempfile
9
- from datetime import date
10
- from pathlib import Path
11
- from typing import Any, Optional
12
-
13
-
14
- def yaml_quote(value: Any) -> str:
15
- return json.dumps("" if value is None else str(value), ensure_ascii=False)
16
-
17
-
18
- CODE_FENCE = chr(96) * 3
19
- DEFAULT_DOCLING_TIMEOUT_SECONDS = 900.0
20
-
21
-
22
- def read_timeout_seconds(env_name: str, default_seconds: float) -> float:
23
- raw_value = os.environ.get(env_name)
24
- if raw_value is None or raw_value.strip() == "":
25
- return default_seconds
26
-
27
- try:
28
- timeout_seconds = float(raw_value)
29
- except ValueError:
30
- return default_seconds
31
-
32
- return timeout_seconds if timeout_seconds > 0 else default_seconds
33
-
34
-
35
- DOCLING_TIMEOUT_SECONDS = read_timeout_seconds(
36
- "VERYFRONT_KNOWLEDGE_DOCLING_TIMEOUT_SECONDS",
37
- DEFAULT_DOCLING_TIMEOUT_SECONDS,
38
- )
39
- TEXT_FILE_EXTENSIONS = {
40
- ".c",
41
- ".cc",
42
- ".conf",
43
- ".cpp",
44
- ".cs",
45
- ".css",
46
- ".go",
47
- ".h",
48
- ".hpp",
49
- ".ini",
50
- ".java",
51
- ".js",
52
- ".jsonl",
53
- ".jsx",
54
- ".kt",
55
- ".less",
56
- ".lua",
57
- ".mjs",
58
- ".ndjson",
59
- ".php",
60
- ".pl",
61
- ".py",
62
- ".r",
63
- ".rb",
64
- ".rs",
65
- ".sass",
66
- ".scala",
67
- ".scss",
68
- ".sh",
69
- ".sql",
70
- ".swift",
71
- ".toml",
72
- ".ts",
73
- ".tsx",
74
- ".xml",
75
- ".yaml",
76
- ".yml",
77
- ".zsh",
78
- }
79
- TEXT_FILE_NAMES = {
80
- "dockerfile",
81
- "makefile",
82
- "readme",
83
- "license",
84
- "changelog",
85
- }
86
-
87
-
88
- def slugify(value: str) -> str:
89
- slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
90
- return slug or "document"
91
-
92
-
93
- def titleize_filename(path: Path) -> str:
94
- text = path.stem.replace("_", " ").replace("-", " ").strip()
95
- return text.title() or path.name
96
-
97
-
98
- def clean_text(value: str) -> str:
99
- text = value.replace("\r\n", "\n").replace("\r", "\n")
100
- text = re.sub(r"\n{3,}", "\n\n", text)
101
- return text.strip()
102
-
103
-
104
- def table_to_markdown(rows: list[list[Any]]) -> str:
105
- if not rows:
106
- return ""
107
-
108
- normalized: list[list[str]] = []
109
- max_cols = 0
110
- for row in rows:
111
- normalized_row = [str(cell or "").replace("|", "\\|").replace("\n", " ").strip() for cell in row]
112
- max_cols = max(max_cols, len(normalized_row))
113
- normalized.append(normalized_row)
114
-
115
- if max_cols == 0:
116
- return ""
117
-
118
- for row in normalized:
119
- while len(row) < max_cols:
120
- row.append("")
121
-
122
- header = normalized[0]
123
- body = normalized[1:]
124
- lines = [
125
- "| " + " | ".join(header) + " |",
126
- "| " + " | ".join(["---"] * max_cols) + " |",
127
- ]
128
- for row in body:
129
- lines.append("| " + " | ".join(row) + " |")
130
- return "\n".join(lines)
131
-
132
-
133
- def build_frontmatter(source: str, source_type: str, description: str) -> str:
134
- return "\n".join([
135
- "---",
136
- f"source: {yaml_quote(source)}",
137
- f"source_type: {yaml_quote(source_type)}",
138
- f"added: {yaml_quote(date.today().isoformat())}",
139
- f"description: {yaml_quote(description)}",
140
- "---",
141
- ])
142
-
143
-
144
- def build_docling_stats(content: str):
145
- return {
146
- "characters": len(content),
147
- "lines": len(content.splitlines()) if content else 0,
148
- "engine": "docling",
149
- }
150
-
151
-
152
- def run_docling_markdown(path: str):
153
- with tempfile.TemporaryDirectory(prefix="veryfront-docling-") as output_dir:
154
- try:
155
- completed = subprocess.run(
156
- [
157
- "docling",
158
- path,
159
- "--to",
160
- "md",
161
- "--image-export-mode",
162
- "placeholder",
163
- "--output",
164
- output_dir,
165
- ],
166
- capture_output=True,
167
- text=True,
168
- check=False,
169
- timeout=DOCLING_TIMEOUT_SECONDS,
170
- )
171
- except subprocess.TimeoutExpired as error:
172
- raise RuntimeError(
173
- f"docling conversion timed out after {DOCLING_TIMEOUT_SECONDS:g}s"
174
- ) from error
175
-
176
- if completed.returncode != 0:
177
- detail = completed.stderr.strip() or completed.stdout.strip() or f"exit code {completed.returncode}"
178
- raise RuntimeError(f"docling conversion failed: {detail}")
179
-
180
- markdown_files = sorted(Path(output_dir).rglob("*.md"))
181
- if not markdown_files:
182
- raise RuntimeError("docling conversion did not produce a markdown file")
183
-
184
- return markdown_files[0].read_text(encoding="utf-8")
185
-
186
-
187
- def parse_with_docling(path: str):
188
- warnings: list[str] = []
189
- normalized_content = clean_text(run_docling_markdown(path))
190
- stats = build_docling_stats(normalized_content)
191
- return normalized_content or "_No extractable text found in document._", stats, warnings
192
-
193
-
194
- def prefer_docling(fallback_parser):
195
- def parser(path: str):
196
- try:
197
- return parse_with_docling(path)
198
- except FileNotFoundError as error:
199
- if getattr(error, "filename", "") == "docling":
200
- return fallback_parser(path)
201
- raise
202
- except RuntimeError as error:
203
- content, stats, warnings = fallback_parser(path)
204
- warnings.append(
205
- "docling conversion failed; fell back to the built-in parser: "
206
- + str(error)
207
- )
208
- return content, stats, warnings
209
-
210
- return parser
211
-
212
-
213
- def build_parser(fallback_parser, prefers_docling: bool):
214
- return prefer_docling(fallback_parser) if prefers_docling else fallback_parser
215
-
216
-
217
- def parse_csv_like(path: str, delimiter: str = ","):
218
- warnings: list[str] = []
219
- with open(path, newline="", encoding="utf-8-sig") as file:
220
- reader = csv.reader(file, delimiter=delimiter)
221
- rows = list(reader)
222
-
223
- if not rows:
224
- return "_Empty file._", {"rows": 0, "columns": 0}, warnings
225
-
226
- header = rows[0]
227
- data = rows[1:]
228
- limited_rows = [header] + data[:200]
229
- parts = [
230
- f"**Rows:** {len(data)} | **Columns:** {len(header)}",
231
- "",
232
- table_to_markdown(limited_rows),
233
- ]
234
- if len(data) > 200:
235
- warnings.append(f"Truncated {len(data) - 200} rows from markdown output")
236
- parts.append(f"\n_...and {len(data) - 200} more rows (truncated)._")
237
- stats = {"rows": len(data), "columns": len(header)}
238
- return "\n".join(parts).strip(), stats, warnings
239
-
240
-
241
- def parse_pdf(path: str):
242
- import pdfplumber
243
-
244
- warnings: list[str] = []
245
- sections: list[str] = []
246
- page_count = 0
247
- table_count = 0
248
-
249
- with pdfplumber.open(path) as pdf:
250
- for index, page in enumerate(pdf.pages, 1):
251
- page_count += 1
252
- text = (page.extract_text() or "").strip()
253
- tables = page.extract_tables() or []
254
- table_count += len(tables)
255
-
256
- parts = [f"## Page {index}"]
257
- if text:
258
- parts.append(text)
259
-
260
- for table_index, table in enumerate(tables, 1):
261
- if table:
262
- parts.append(f"\n### Table {table_index}")
263
- parts.append(table_to_markdown(table))
264
-
265
- sections.append("\n\n".join(parts).strip())
266
-
267
- content = "\n\n---\n\n".join(section for section in sections if section)
268
- stats = {"pages": page_count, "tables": table_count}
269
- return content or "_No extractable text found in PDF._", stats, warnings
270
-
271
-
272
- def iter_docx_elements(document):
273
- from docx.oxml.ns import qn
274
-
275
- body = document.element.body
276
- for child in body:
277
- if child.tag == qn("w:p"):
278
- paragraph = next((p for p in document.paragraphs if p._element is child), None)
279
- if paragraph:
280
- yield {
281
- "type": "paragraph",
282
- "text": paragraph.text,
283
- "style": paragraph.style.name if paragraph.style else None,
284
- }
285
- elif child.tag == qn("w:tbl"):
286
- table = next((t for t in document.tables if t._element is child), None)
287
- if table:
288
- rows = []
289
- for row in table.rows:
290
- rows.append([cell.text.strip() for cell in row.cells])
291
- yield {"type": "table", "rows": rows}
292
-
293
-
294
- def parse_docx(path: str):
295
- from docx import Document
296
-
297
- warnings: list[str] = []
298
- document = Document(path)
299
- parts: list[str] = []
300
- paragraph_count = 0
301
- table_count = 0
302
-
303
- for element in iter_docx_elements(document):
304
- if element["type"] == "paragraph":
305
- text = element["text"].strip()
306
- if not text:
307
- continue
308
- paragraph_count += 1
309
- style = element["style"] or ""
310
- if "Heading 1" in style:
311
- parts.append(f"# {text}")
312
- elif "Heading 2" in style:
313
- parts.append(f"## {text}")
314
- elif "Heading 3" in style:
315
- parts.append(f"### {text}")
316
- elif "List" in style:
317
- parts.append(f"- {text}")
318
- else:
319
- parts.append(text)
320
- elif element["type"] == "table":
321
- table_count += 1
322
- parts.append(table_to_markdown(element["rows"]))
323
-
324
- stats = {"paragraphs": paragraph_count, "tables": table_count}
325
- return "\n\n".join(part for part in parts if part).strip(), stats, warnings
326
-
327
-
328
- def parse_excel(path: str):
329
- import pandas as pd
330
-
331
- warnings: list[str] = []
332
- workbook = pd.read_excel(path, sheet_name=None, dtype=str)
333
- parts: list[str] = []
334
- sheet_names: list[str] = []
335
- total_rows = 0
336
-
337
- for sheet_name, frame in workbook.items():
338
- sheet_names.append(sheet_name)
339
- frame = frame.fillna("")
340
- rows = frame.values.tolist()
341
- header = [str(column) for column in frame.columns.tolist()]
342
- total_rows += len(rows)
343
- parts.append(f"## Sheet: {sheet_name}")
344
- parts.append(f"**Rows:** {len(rows)} | **Columns:** {len(header)}")
345
- limited_rows = [header] + rows[:200]
346
- parts.append(table_to_markdown(limited_rows))
347
- if len(rows) > 200:
348
- warnings.append(f"Truncated {len(rows) - 200} rows from sheet {sheet_name}")
349
- parts.append(f"_...and {len(rows) - 200} more rows (truncated)._")
350
-
351
- stats = {"sheets": len(sheet_names), "rows": total_rows, "sheet_names": sheet_names}
352
- return "\n\n".join(part for part in parts if part).strip(), stats, warnings
353
-
354
-
355
- def parse_pptx(path: str):
356
- from pptx import Presentation
357
-
358
- warnings: list[str] = []
359
- presentation = Presentation(path)
360
- parts: list[str] = []
361
- slide_count = 0
362
- table_count = 0
363
-
364
- for index, slide in enumerate(presentation.slides, 1):
365
- slide_count += 1
366
- texts: list[str] = []
367
- for shape in slide.shapes:
368
- if getattr(shape, "has_text_frame", False):
369
- for paragraph in shape.text_frame.paragraphs:
370
- text = paragraph.text.strip()
371
- if text:
372
- texts.append(text)
373
- if getattr(shape, "has_table", False):
374
- table_count += 1
375
- rows = []
376
- for row in shape.table.rows:
377
- rows.append([cell.text.strip() for cell in row.cells])
378
- texts.append(table_to_markdown(rows))
379
- if texts:
380
- parts.append(f"## Slide {index}")
381
- parts.append("\n\n".join(texts))
382
-
383
- stats = {"slides": slide_count, "tables": table_count}
384
- return "\n\n".join(parts).strip(), stats, warnings
385
-
386
-
387
- def parse_html(path: str):
388
- from bs4 import BeautifulSoup
389
-
390
- warnings: list[str] = []
391
- with open(path, encoding="utf-8") as file:
392
- soup = BeautifulSoup(file.read(), "lxml")
393
-
394
- for tag in soup(["script", "style", "nav", "footer", "header"]):
395
- tag.decompose()
396
-
397
- table_parts: list[str] = []
398
- for table_index, table in enumerate(soup.find_all("table"), 1):
399
- rows = []
400
- for row in table.find_all("tr"):
401
- rows.append([cell.get_text(" ", strip=True) for cell in row.find_all(["th", "td"])])
402
- if rows:
403
- table_parts.append(f"### Table {table_index}")
404
- table_parts.append(table_to_markdown(rows))
405
- table.decompose()
406
-
407
- text = clean_text(soup.get_text("\n"))
408
- parts = [text] if text else []
409
- if table_parts:
410
- parts.extend(table_parts)
411
-
412
- stats = {"tables": len(table_parts) // 2, "characters": len(text)}
413
- return "\n\n".join(part for part in parts if part).strip(), stats, warnings
414
-
415
-
416
- def parse_text(path: str):
417
- warnings: list[str] = []
418
- with open(path, encoding="utf-8") as file:
419
- text = clean_text(file.read())
420
- stats = {"characters": len(text), "lines": len(text.splitlines()) if text else 0}
421
- return text, stats, warnings
422
-
423
-
424
- def parse_json(path: str):
425
- warnings: list[str] = []
426
- with open(path, encoding="utf-8") as file:
427
- data = json.load(file)
428
-
429
- if isinstance(data, list) and data and isinstance(data[0], dict):
430
- headers = list(data[0].keys())
431
- rows = [headers] + [[row.get(header, "") for header in headers] for row in data[:200]]
432
- content_parts = [
433
- f"**Records:** {len(data)} | **Fields:** {len(headers)}",
434
- "",
435
- table_to_markdown(rows),
436
- ]
437
- if len(data) > 200:
438
- warnings.append(f"Truncated {len(data) - 200} records from markdown output")
439
- content_parts.append(f"\n_...and {len(data) - 200} more records (truncated)._")
440
- stats = {"records": len(data), "fields": len(headers)}
441
- return "\n".join(content_parts).strip(), stats, warnings
442
-
443
- rendered = json.dumps(data, indent=2, ensure_ascii=False)
444
- stats = {"top_level_type": type(data).__name__}
445
- return f"{CODE_FENCE}json\n{rendered}\n{CODE_FENCE}", stats, warnings
446
-
447
-
448
- def select_parser_definition(path: Path):
449
- ext = path.suffix.lower()
450
- name = path.name.lower()
451
- if ext == ".pdf":
452
- return "pdf", parse_pdf, True
453
- if ext in {".csv", ".tsv"}:
454
- delimiter = "\t" if ext == ".tsv" else ","
455
- return ext.lstrip("."), lambda file_path: parse_csv_like(file_path, delimiter), False
456
- if ext in {".xlsx", ".xls"}:
457
- source_type = ext.lstrip(".")
458
- return source_type, parse_excel, True
459
- if ext == ".docx":
460
- return "docx", parse_docx, True
461
- if ext == ".pptx":
462
- return "pptx", parse_pptx, True
463
- if ext in {".html", ".htm"}:
464
- return "html", parse_html, True
465
- if ext in {".txt", ".md", ".mdx"}:
466
- return ext.lstrip("."), parse_text, False
467
- if ext == ".json":
468
- return "json", parse_json, False
469
- if ext in TEXT_FILE_EXTENSIONS:
470
- return ext.lstrip("."), parse_text, False
471
- if not ext and name in TEXT_FILE_NAMES:
472
- return "text", parse_text, False
473
- raise ValueError(f"Unsupported file type: {ext}")
474
-
475
-
476
- def select_parser(path: Path):
477
- source_type, fallback_parser, prefers_docling = select_parser_definition(path)
478
- return source_type, build_parser(fallback_parser, prefers_docling)
479
-
480
-
481
- def build_summary(source_type: str, stats: dict[str, Any]) -> str:
482
- if stats.get("engine") == "docling":
483
- return f"Converted {source_type.upper()} to markdown ({stats.get('characters', 0)} chars)."
484
- if source_type in {"csv", "tsv"}:
485
- return f"Parsed {stats.get('rows', 0)} rows across {stats.get('columns', 0)} columns."
486
- if source_type in {"xlsx", "xls"}:
487
- return f"Parsed {stats.get('sheets', 0)} sheet(s) with {stats.get('rows', 0)} total rows."
488
- if source_type == "pdf":
489
- return f"Extracted {stats.get('pages', 0)} page(s) and {stats.get('tables', 0)} table(s)."
490
- if source_type == "docx":
491
- return f"Extracted {stats.get('paragraphs', 0)} paragraphs and {stats.get('tables', 0)} tables."
492
- if source_type == "pptx":
493
- return f"Extracted {stats.get('slides', 0)} slide(s)."
494
- if source_type == "json":
495
- if "records" in stats:
496
- return f"Parsed {stats.get('records', 0)} record(s) across {stats.get('fields', 0)} fields."
497
- return f"Converted JSON ({stats.get('top_level_type', 'object')}) to markdown."
498
- if source_type == "html":
499
- return f"Converted HTML with {stats.get('tables', 0)} table(s) to markdown."
500
- return f"Converted document to markdown ({stats.get('characters', 0)} chars)."
501
-
502
-
503
- def ingest_document_to_knowledge(file_path: str, output_dir: Optional[str] = None, description: Optional[str] = None, slug: Optional[str] = None, source_reference: Optional[str] = None):
504
- path = Path(file_path)
505
- if not path.exists():
506
- raise FileNotFoundError(f"File not found: {file_path}")
507
-
508
- output_root = Path(output_dir or "/workspace/knowledge")
509
- output_root.mkdir(parents=True, exist_ok=True)
510
-
511
- if not slug:
512
- slug = slugify(path.stem)
513
-
514
- source_type, parser = select_parser(path)
515
- content, stats, warnings = parser(str(path))
516
- content = clean_text(content)
517
-
518
- resolved_description = description or f"Parsed from {path.name}"
519
- title = titleize_filename(path)
520
- frontmatter = build_frontmatter(source_reference or path.name, source_type, resolved_description)
521
- markdown = f"{frontmatter}\n\n# {title}\n\n{content}\n"
522
-
523
- output_path = output_root / f"{slug}.md"
524
- output_path.write_text(markdown, encoding="utf-8")
525
-
526
- return {
527
- "success": True,
528
- "source_path": str(path),
529
- "source_filename": path.name,
530
- "source_type": source_type,
531
- "slug": slug,
532
- "sandbox_output_path": str(output_path),
533
- "suggested_project_path": f"knowledge/{slug}.md",
534
- "description": resolved_description,
535
- "title": title,
536
- "summary": build_summary(source_type, stats),
537
- "stats": stats,
538
- "warnings": warnings,
539
- }
540
-
541
-
542
- def ingest_documents_to_knowledge(documents: list[dict[str, Any]], output_dir: Optional[str] = None):
543
- output_root = Path(output_dir or "/workspace/knowledge")
544
- output_root.mkdir(parents=True, exist_ok=True)
545
-
546
- prepared_documents: list[dict[str, Any]] = []
547
- for index, document in enumerate(documents):
548
- file_path = document["file_path"]
549
- path = Path(file_path)
550
- if not path.exists():
551
- raise FileNotFoundError(f"File not found: {file_path}")
552
-
553
- slug = document.get("slug") or slugify(path.stem)
554
- source_type, fallback_parser, prefers_docling = select_parser_definition(path)
555
- prepared_documents.append({
556
- "index": index,
557
- "path": path,
558
- "slug": slug,
559
- "description": document.get("description"),
560
- "source_reference": document.get("source_reference"),
561
- "source_type": source_type,
562
- "fallback_parser": fallback_parser,
563
- "prefers_docling": prefers_docling,
564
- })
565
-
566
- results = []
567
- for document in prepared_documents:
568
- parser = build_parser(
569
- document["fallback_parser"],
570
- document["prefers_docling"],
571
- )
572
- content, stats, warnings = parser(str(document["path"]))
573
-
574
- content = clean_text(content)
575
- resolved_description = document["description"] or f"Parsed from {document['path'].name}"
576
- title = titleize_filename(document["path"])
577
- frontmatter = build_frontmatter(
578
- document["source_reference"] or document["path"].name,
579
- document["source_type"],
580
- resolved_description,
581
- )
582
- markdown = f"{frontmatter}\n\n# {title}\n\n{content}\n"
583
-
584
- output_path = output_root / f"{document['slug']}.md"
585
- output_path.write_text(markdown, encoding="utf-8")
586
-
587
- results.append({
588
- "success": True,
589
- "source_path": str(document["path"]),
590
- "source_filename": document["path"].name,
591
- "source_type": document["source_type"],
592
- "slug": document["slug"],
593
- "sandbox_output_path": str(output_path),
594
- "suggested_project_path": f"knowledge/{document['slug']}.md",
595
- "description": resolved_description,
596
- "title": title,
597
- "summary": build_summary(document["source_type"], stats),
598
- "stats": stats,
599
- "warnings": warnings,
600
- })
601
-
602
- return results
603
-
604
-
605
- def main():
606
- parser = argparse.ArgumentParser(description="Convert a local document into knowledge-base markdown")
607
- parser.add_argument("--input-json", required=True)
608
- parser.add_argument("--output-json", required=True)
609
- args = parser.parse_args()
610
-
611
- try:
612
- payload = json.loads(Path(args.input_json).read_text(encoding="utf-8"))
613
- files_payload = payload.get("files")
614
- if isinstance(files_payload, list):
615
- result = ingest_documents_to_knowledge(
616
- documents=files_payload,
617
- output_dir=payload.get("output_dir"),
618
- )
619
- else:
620
- result = ingest_document_to_knowledge(
621
- file_path=payload["file_path"],
622
- output_dir=payload.get("output_dir"),
623
- description=payload.get("description"),
624
- slug=payload.get("slug"),
625
- source_reference=payload.get("source_reference"),
626
- )
627
- except ModuleNotFoundError as error:
628
- missing_package = error.name or "required package"
629
- raise SystemExit(
630
- "Missing Python package '"
631
- + missing_package
632
- + "'. Install knowledge parser dependencies with: "
633
- + "pip install docling pandas openpyxl xlrd pdfplumber python-docx python-pptx beautifulsoup4 lxml"
634
- )
635
-
636
- Path(args.output_json).write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
637
-
638
-
639
- if __name__ == "__main__":
640
- main()
641
- `;
@@ -1,2 +0,0 @@
1
- export declare function sanitizeJobOutputForLogging(value: unknown): unknown;
2
- //# sourceMappingURL=sanitize-job-output.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sanitize-job-output.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/sanitize-job-output.ts"],"names":[],"mappings":"AAoBA,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEnE"}
@@ -1,2 +0,0 @@
1
- export declare function writeJobResultIfConfigured(value: unknown): Promise<void>;
2
- //# sourceMappingURL=write-job-result.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"write-job-result.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/write-job-result.ts"],"names":[],"mappings":"AAWA,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9E"}
@@ -1,16 +0,0 @@
1
- import * as dntShim from "../../_dnt.shims.js";
2
- import { dirname } from "../../src/platform/compat/path/index.js";
3
- import { sanitizeJobOutputForLogging } from "./sanitize-job-output.js";
4
- const JOB_RESULT_PATH_ENV = "VERYFRONT_JOB_RESULT_PATH";
5
- function getJobResultPath() {
6
- const value = dntShim.Deno.env.get(JOB_RESULT_PATH_ENV)?.trim();
7
- return value ? value : null;
8
- }
9
- export async function writeJobResultIfConfigured(value) {
10
- const resultPath = getJobResultPath();
11
- if (!resultPath) {
12
- return;
13
- }
14
- await dntShim.Deno.mkdir(dirname(resultPath), { recursive: true });
15
- await dntShim.Deno.writeTextFile(resultPath, JSON.stringify(sanitizeJobOutputForLogging(value), null, 2));
16
- }