pal-code 0.1.0__py3-none-any.whl

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 (282) hide show
  1. pal/__init__.py +29 -0
  2. pal/__main__.py +3 -0
  3. pal/access_manager/__init__.py +16 -0
  4. pal/access_manager/components/__init__.py +14 -0
  5. pal/access_manager/components/access_control.py +41 -0
  6. pal/access_manager/components/base.py +39 -0
  7. pal/access_manager/components/intervention.py +31 -0
  8. pal/access_manager/components/privilege_registry.py +30 -0
  9. pal/access_manager/pipeline.py +68 -0
  10. pal/access_manager/utils/__init__.py +2 -0
  11. pal/agent_constraints/__init__.py +3 -0
  12. pal/agent_constraints/components/AGENT_OUTPUT.md +13 -0
  13. pal/agent_constraints/components/__init__.py +3 -0
  14. pal/agent_constraints/components/constraints.py +22 -0
  15. pal/agent_constraints/pipeline.py +22 -0
  16. pal/agent_constraints/utils/__init__.py +2 -0
  17. pal/agents/__init__.py +12 -0
  18. pal/agents/coding.py +14 -0
  19. pal/agents/docs.py +15 -0
  20. pal/chat/__init__.py +4 -0
  21. pal/chat/demo.py +231 -0
  22. pal/chat/providers.py +23 -0
  23. pal/chat/service.py +591 -0
  24. pal/chat/services/__init__.py +26 -0
  25. pal/chat/services/base.py +142 -0
  26. pal/chat/services/data.py +142 -0
  27. pal/chat/services/media.py +123 -0
  28. pal/chat/services/web.py +60 -0
  29. pal/chat/sessions.py +108 -0
  30. pal/chat/tools.py +446 -0
  31. pal/chat/uploads.py +81 -0
  32. pal/cli.py +502 -0
  33. pal/compression/__init__.py +12 -0
  34. pal/compression/components/__init__.py +64 -0
  35. pal/compression/components/adverbs.py +20 -0
  36. pal/compression/components/hedges.py +54 -0
  37. pal/compression/components/interjections.py +20 -0
  38. pal/compression/components/minifier.py +56 -0
  39. pal/compression/components/output_compressor.py +13 -0
  40. pal/compression/components/pleonasms.py +54 -0
  41. pal/compression/components/punctuations.py +36 -0
  42. pal/compression/components/stopwords.py +62 -0
  43. pal/compression/history.py +346 -0
  44. pal/compression/pipeline.py +58 -0
  45. pal/compression/utils/__init__.py +30 -0
  46. pal/compression/utils/negations.py +18 -0
  47. pal/compression/utils/nltk_setup.py +20 -0
  48. pal/compression/utils/pronouns.py +27 -0
  49. pal/compression/utils/proselint_setup.py +9 -0
  50. pal/compression/utils/spacy_setup.py +22 -0
  51. pal/config/default.yaml +163 -0
  52. pal/config.py +504 -0
  53. pal/context_manager/__init__.py +7 -0
  54. pal/context_manager/chat_context.py +117 -0
  55. pal/context_manager/components/__init__.py +5 -0
  56. pal/context_manager/components/assembler.py +75 -0
  57. pal/context_manager/components/context_cache.py +45 -0
  58. pal/context_manager/components/kv_bridge.py +16 -0
  59. pal/context_manager/components/pager.py +48 -0
  60. pal/context_manager/components/simple_context_manager.py +56 -0
  61. pal/context_manager/components/turns.py +88 -0
  62. pal/context_manager/pipeline.py +27 -0
  63. pal/context_manager/utils/__init__.py +18 -0
  64. pal/context_manager/utils/hashing.py +5 -0
  65. pal/context_manager/utils/relevance.py +59 -0
  66. pal/context_manager/utils/storage.py +37 -0
  67. pal/documents/__init__.py +4 -0
  68. pal/documents/components/__init__.py +0 -0
  69. pal/documents/components/chunker.py +79 -0
  70. pal/documents/components/index.py +155 -0
  71. pal/documents/utils/__init__.py +1 -0
  72. pal/errors.py +40 -0
  73. pal/input_converters/__init__.py +8 -0
  74. pal/input_converters/components/__init__.py +0 -0
  75. pal/input_converters/components/base.py +38 -0
  76. pal/input_converters/components/pandoc_converter.py +87 -0
  77. pal/input_converters/components/pdf_converter.py +46 -0
  78. pal/input_converters/components/pptx_converter.py +52 -0
  79. pal/input_converters/components/xlsx_converter.py +38 -0
  80. pal/input_converters/pipeline.py +72 -0
  81. pal/input_converters/utils/__init__.py +1 -0
  82. pal/input_converters/workspace.py +56 -0
  83. pal/input_savers/INPUT_RULES.md +2 -0
  84. pal/input_savers/__init__.py +5 -0
  85. pal/input_savers/components/__init__.py +0 -0
  86. pal/input_savers/components/base.py +40 -0
  87. pal/input_savers/components/methods.py +41 -0
  88. pal/input_savers/components/verifier.py +39 -0
  89. pal/input_savers/pipeline.py +154 -0
  90. pal/input_savers/utils/__init__.py +0 -0
  91. pal/input_savers/utils/guards.py +94 -0
  92. pal/knowledge_graph/__init__.py +10 -0
  93. pal/knowledge_graph/components/__init__.py +0 -0
  94. pal/knowledge_graph/components/base.py +50 -0
  95. pal/knowledge_graph/components/builder.py +54 -0
  96. pal/knowledge_graph/components/retriever.py +100 -0
  97. pal/knowledge_graph/components/workspace.py +116 -0
  98. pal/knowledge_graph/pipeline.py +215 -0
  99. pal/knowledge_graph/utils/__init__.py +1 -0
  100. pal/latency_tracker/__init__.py +3 -0
  101. pal/latency_tracker/pipeline.py +86 -0
  102. pal/llm_scheduler/__init__.py +11 -0
  103. pal/llm_scheduler/api.py +137 -0
  104. pal/llm_scheduler/code.py +28 -0
  105. pal/llm_scheduler/core.py +92 -0
  106. pal/llm_scheduler/local.py +215 -0
  107. pal/llm_scheduler/pipeline.py +42 -0
  108. pal/llm_scheduler/types.py +92 -0
  109. pal/llm_wrapper/__init__.py +34 -0
  110. pal/llm_wrapper/adapter.py +316 -0
  111. pal/llm_wrapper/components/__init__.py +23 -0
  112. pal/llm_wrapper/components/adapter_types.py +90 -0
  113. pal/llm_wrapper/components/base.py +195 -0
  114. pal/llm_wrapper/components/features.py +104 -0
  115. pal/llm_wrapper/components/litellm_wrapper.py +215 -0
  116. pal/llm_wrapper/components/openrouter_wrapper.py +114 -0
  117. pal/llm_wrapper/modes.py +73 -0
  118. pal/llm_wrapper/pipeline.py +61 -0
  119. pal/llm_wrapper/utils/__init__.py +4 -0
  120. pal/llm_wrapper/utils/registry.py +49 -0
  121. pal/llm_wrapper/utils/token_counting.py +10 -0
  122. pal/local_llm/__init__.py +5 -0
  123. pal/local_llm/components/__init__.py +0 -0
  124. pal/local_llm/components/huggingface_local.py +217 -0
  125. pal/local_llm/components/local_llm.py +60 -0
  126. pal/logging_setup.py +18 -0
  127. pal/memory/__init__.py +7 -0
  128. pal/memory/components/__init__.py +6 -0
  129. pal/memory/components/lru_k.py +56 -0
  130. pal/memory/components/manager.py +98 -0
  131. pal/memory/components/note.py +54 -0
  132. pal/memory/components/storage_manager.py +60 -0
  133. pal/memory/pipeline.py +122 -0
  134. pal/memory/utils/__init__.py +3 -0
  135. pal/memory/utils/keyword_overlap.py +3 -0
  136. pal/memory_tracker/__init__.py +3 -0
  137. pal/memory_tracker/components/__init__.py +0 -0
  138. pal/memory_tracker/components/process.py +56 -0
  139. pal/memory_tracker/pipeline.py +44 -0
  140. pal/metrics/__init__.py +4 -0
  141. pal/metrics/components/__init__.py +4 -0
  142. pal/metrics/components/collector.py +140 -0
  143. pal/metrics/components/exporter.py +28 -0
  144. pal/metrics/pipeline.py +49 -0
  145. pal/metrics/utils/__init__.py +2 -0
  146. pal/notes_manager/__init__.py +5 -0
  147. pal/notes_manager/components/__init__.py +0 -0
  148. pal/notes_manager/components/privacy.py +14 -0
  149. pal/notes_manager/components/recall.py +31 -0
  150. pal/notes_manager/components/tidy.py +28 -0
  151. pal/notes_manager/pipeline.py +138 -0
  152. pal/orchestrator/__init__.py +26 -0
  153. pal/orchestrator/components/__init__.py +26 -0
  154. pal/orchestrator/components/agent.py +105 -0
  155. pal/orchestrator/components/cgroup_manager.py +209 -0
  156. pal/orchestrator/components/context_interrupt.py +220 -0
  157. pal/orchestrator/components/limits.py +9 -0
  158. pal/orchestrator/components/scheduler.py +69 -0
  159. pal/orchestrator/components/syscall_scheduler.py +200 -0
  160. pal/orchestrator/pipeline.py +228 -0
  161. pal/orchestrator/utils/__init__.py +4 -0
  162. pal/orchestrator/utils/device.py +164 -0
  163. pal/orchestrator/utils/protocol.py +32 -0
  164. pal/os_kernel/__init__.py +5 -0
  165. pal/os_kernel/filesystem.py +80 -0
  166. pal/os_kernel/network.py +146 -0
  167. pal/os_kernel/pipeline.py +175 -0
  168. pal/os_kernel/sysinfo.py +36 -0
  169. pal/paths.py +59 -0
  170. pal/pipeline.py +177 -0
  171. pal/py.typed +0 -0
  172. pal/redact.py +19 -0
  173. pal/sdk/__init__.py +33 -0
  174. pal/sdk/adapters.py +43 -0
  175. pal/sdk/agent.py +99 -0
  176. pal/sdk/components/__init__.py +14 -0
  177. pal/sdk/components/autogen_adapter.py +65 -0
  178. pal/sdk/components/autogen_client.py +98 -0
  179. pal/sdk/components/framework_adapter_base.py +69 -0
  180. pal/sdk/components/metagpt_adapter.py +46 -0
  181. pal/sdk/components/open_interpreter_adapter.py +47 -0
  182. pal/sdk/components/tool_catalog.py +44 -0
  183. pal/sdk/os_api.py +84 -0
  184. pal/sdk/patterns.py +129 -0
  185. pal/sdk/pipeline.py +240 -0
  186. pal/sdk/queries.py +68 -0
  187. pal/sdk/savers.py +88 -0
  188. pal/sdk/utils/__init__.py +2 -0
  189. pal/storage_manager/__init__.py +17 -0
  190. pal/storage_manager/components/__init__.py +19 -0
  191. pal/storage_manager/components/base.py +50 -0
  192. pal/storage_manager/components/local_file_backend.py +84 -0
  193. pal/storage_manager/components/storage_queue.py +91 -0
  194. pal/storage_manager/components/vector_store_backend.py +122 -0
  195. pal/storage_manager/pipeline.py +153 -0
  196. pal/storage_manager/utils/__init__.py +4 -0
  197. pal/storage_manager/utils/memory_adapter.py +60 -0
  198. pal/storage_manager/utils/sdk.py +93 -0
  199. pal/studio/__init__.py +0 -0
  200. pal/studio/builder.py +166 -0
  201. pal/studio/catalog.py +90 -0
  202. pal/studio/checks.py +129 -0
  203. pal/studio/clients.py +6 -0
  204. pal/studio/editor.py +137 -0
  205. pal/studio/files.py +6 -0
  206. pal/studio/models.py +6 -0
  207. pal/studio/plan.py +149 -0
  208. pal/studio/planner.py +205 -0
  209. pal/studio/policy.py +69 -0
  210. pal/studio/routes.py +254 -0
  211. pal/studio/runs.py +210 -0
  212. pal/task_router/__init__.py +11 -0
  213. pal/task_router/components/__init__.py +0 -0
  214. pal/task_router/components/answerer.py +37 -0
  215. pal/task_router/components/base.py +34 -0
  216. pal/task_router/components/classifier.py +40 -0
  217. pal/task_router/components/gates.py +23 -0
  218. pal/task_router/pipeline.py +61 -0
  219. pal/task_router/utils/__init__.py +1 -0
  220. pal/terminal/__init__.py +1 -0
  221. pal/terminal/backend.py +141 -0
  222. pal/terminal/bench.py +173 -0
  223. pal/terminal/cli.py +826 -0
  224. pal/terminal/clients.py +170 -0
  225. pal/terminal/commands.py +401 -0
  226. pal/terminal/decide.py +242 -0
  227. pal/terminal/files.py +55 -0
  228. pal/terminal/flow_cli.py +439 -0
  229. pal/terminal/hardware.py +199 -0
  230. pal/terminal/history.py +88 -0
  231. pal/terminal/keys.py +83 -0
  232. pal/terminal/local_models.py +261 -0
  233. pal/terminal/logo.py +117 -0
  234. pal/terminal/models.py +99 -0
  235. pal/terminal/policy.py +54 -0
  236. pal/terminal/project.py +413 -0
  237. pal/terminal/review.py +65 -0
  238. pal/terminal/security.py +239 -0
  239. pal/terminal/session.py +503 -0
  240. pal/terminal/setup.py +354 -0
  241. pal/terminal/syntax.py +98 -0
  242. pal/terminal/theme.py +37 -0
  243. pal/terminal/ui.py +647 -0
  244. pal/terminal/voice.py +137 -0
  245. pal/terminal/workflow.py +624 -0
  246. pal/token_tracker/__init__.py +3 -0
  247. pal/token_tracker/pipeline.py +54 -0
  248. pal/tokens.py +21 -0
  249. pal/tool_catalog.py +107 -0
  250. pal/tool_manager/__init__.py +17 -0
  251. pal/tool_manager/builtin.py +82 -0
  252. pal/tool_manager/components/__init__.py +19 -0
  253. pal/tool_manager/components/base.py +53 -0
  254. pal/tool_manager/components/conflict_resolver.py +27 -0
  255. pal/tool_manager/components/param_validator.py +41 -0
  256. pal/tool_manager/components/tool_call_queue.py +111 -0
  257. pal/tool_manager/components/tool_registry.py +60 -0
  258. pal/tool_manager/pipeline.py +80 -0
  259. pal/tool_manager/utils/__init__.py +3 -0
  260. pal/web/__init__.py +3 -0
  261. pal/web/app.py +276 -0
  262. pal/web/static/editor.js +219 -0
  263. pal/web/static/fonts/LICENSE-Inter.txt +3 -0
  264. pal/web/static/fonts/inter-400.woff2 +0 -0
  265. pal/web/static/fonts/inter-500.woff2 +0 -0
  266. pal/web/static/fonts/inter-600.woff2 +0 -0
  267. pal/web/static/fonts/inter-700.woff2 +0 -0
  268. pal/web/static/inter.css +5 -0
  269. pal/web/static/pal-logo-256.png +0 -0
  270. pal/web/static/pal-logo-32.png +0 -0
  271. pal/web/static/pal-logo-64.png +0 -0
  272. pal/web/static/studio.css +306 -0
  273. pal/web/static/studio.js +533 -0
  274. pal/web/static/voice.js +76 -0
  275. pal/web/templates/index.html +368 -0
  276. pal/web/templates/studio.html +120 -0
  277. pal_code-0.1.0.dist-info/METADATA +379 -0
  278. pal_code-0.1.0.dist-info/RECORD +282 -0
  279. pal_code-0.1.0.dist-info/WHEEL +5 -0
  280. pal_code-0.1.0.dist-info/entry_points.txt +2 -0
  281. pal_code-0.1.0.dist-info/licenses/LICENSE +21 -0
  282. pal_code-0.1.0.dist-info/top_level.txt +1 -0
pal/terminal/bench.py ADDED
@@ -0,0 +1,173 @@
1
+ """`pal bench`: the same task, on the same model, two ways, with the tokens the model really used.
2
+
3
+ plain a plain chat: the project's source files are pasted into the first message, and every later message resends the whole
4
+ conversation (what a simple chat client does when you paste your code into it).
5
+ pal Pal: the folder's project map, code graph and documents, with only the passages that match each message sent, and the
6
+ history paged and compressed.
7
+
8
+ Each way works in its own temporary copy of the folder, and applies its own changes there, so the follow-up steps build on them. Your
9
+ folder is never changed. Token counts are the model's own when it reports them (most APIs and Ollama do); the report says when some were
10
+ estimated. The quality of the two results is yours to judge: the report lists the files each way wrote.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import shutil
16
+ import tempfile
17
+ import time
18
+ from collections.abc import Callable
19
+ from dataclasses import asdict, dataclass, field
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ from pal.paths import pal_home
24
+ from pal.terminal.project import MAX_CODE_BYTES, Project, scan
25
+
26
+ DEFAULT_FOLLOW_UPS = ("Add a short automated test for what you just built.", "In three sentences: how does it work, and what would you improve next?")
27
+ PLAIN_PASTE_LIMIT = 400_000 # bytes of source the plain chat pastes (about 100k tokens): beyond that no chat would take it
28
+ STEP_MODES = ("build", "build", "ask")
29
+
30
+
31
+ @dataclass
32
+ class Step:
33
+ message: str
34
+ mode: str
35
+ prompt: int = 0
36
+ completion: int = 0
37
+ seconds: float = 0.0
38
+ files: list[str] = field(default_factory=list)
39
+
40
+ @property
41
+ def total(self) -> int:
42
+ return self.prompt + self.completion
43
+
44
+
45
+ @dataclass
46
+ class Arm:
47
+ name: str
48
+ steps: list[Step] = field(default_factory=list)
49
+ estimated_calls: int = 0
50
+ calls: int = 0
51
+ error: str = ""
52
+
53
+ @property
54
+ def total(self) -> int:
55
+ return sum(s.total for s in self.steps)
56
+
57
+ @property
58
+ def seconds(self) -> float:
59
+ return round(sum(s.seconds for s in self.steps), 1)
60
+
61
+
62
+ @dataclass
63
+ class BenchResult:
64
+ task: str
65
+ model: str
66
+ folder: str
67
+ plain: Arm
68
+ pal: Arm
69
+ when: float = field(default_factory=time.time)
70
+
71
+ @property
72
+ def saved(self) -> int:
73
+ return self.plain.total - self.pal.total
74
+
75
+ @property
76
+ def saved_pct(self) -> float:
77
+ return round(100.0 * self.saved / self.plain.total, 1) if self.plain.total else 0.0
78
+
79
+ def to_dict(self) -> dict:
80
+ d = asdict(self)
81
+ d.update(saved=self.saved, saved_pct=self.saved_pct, plain_total=self.plain.total, pal_total=self.pal.total)
82
+ return d
83
+
84
+
85
+ def copy_folder(src: Path, dst: Path) -> None:
86
+ """The files Pal would see (no dependencies, build output, hidden folders or secrets), copied for one arm of the bench."""
87
+ found = scan(src)
88
+ for rel in list(found.code) + list(found.docs):
89
+ target = dst / rel
90
+ target.parent.mkdir(parents=True, exist_ok=True)
91
+ shutil.copy2(src / rel, target)
92
+ for name in ("package.json", "pyproject.toml", "requirements.txt", "go.mod", "Cargo.toml", "pom.xml", "build.gradle", "build.gradle.kts"):
93
+ if (src / name).is_file() and not (dst / name).exists():
94
+ shutil.copy2(src / name, dst / name)
95
+
96
+
97
+ def source_files(folder: Path, limit: int = PLAIN_PASTE_LIMIT) -> list[dict]:
98
+ """The source files a plain chat pastes, up to `limit` bytes in all."""
99
+ out, total = [], 0
100
+ for rel in sorted(scan(folder).code):
101
+ try:
102
+ text = (folder / rel).read_text(encoding="utf-8", errors="replace")
103
+ except OSError:
104
+ continue
105
+ size = len(text.encode("utf-8"))
106
+ if size > MAX_CODE_BYTES or total + size > limit:
107
+ continue
108
+ total += size
109
+ out.append({"name": rel, "text": text})
110
+ return out
111
+
112
+
113
+ def _run_arm(name: str, session, steps: list[tuple[str, str]], progress: Callable[[str], None]) -> Arm:
114
+ arm = Arm(name)
115
+ for i, (message, mode) in enumerate(steps, 1):
116
+ progress(f"{name}: step {i} of {len(steps)}")
117
+ session.mode = mode
118
+ try:
119
+ turn = session.ask(message)
120
+ except Exception as e: # noqa: BLE001 (a model error ends this arm; the other still runs)
121
+ arm.error = f"{type(e).__name__}: {str(e)[:200]}"
122
+ break
123
+ written = [p for p, status in session.apply(turn.changes) if status == "written"] if turn.changes else []
124
+ arm.steps.append(Step(message, mode, turn.prompt_tokens, turn.completion_tokens, turn.seconds, written))
125
+ counter = getattr(session.client, "main", None) or getattr(session.client, "core", None)
126
+ arm.calls = getattr(counter, "calls", 0)
127
+ arm.estimated_calls = getattr(counter, "estimated", 0)
128
+ return arm
129
+
130
+
131
+ def run_bench(folder: Path, task: str, core_factory: Callable[[], Any], model: str, follow_ups: list[str] | None = None,
132
+ progress: Callable[[str], None] | None = None, home: Path | None = None) -> BenchResult:
133
+ """Run the task and its follow-ups both ways. `core_factory()` makes a fresh model wrapper (one per arm, so nothing is shared)."""
134
+ from pal.terminal.clients import PalClient, PlainClient
135
+ from pal.terminal.session import SYSTEM, PalSession
136
+ progress = progress or (lambda _m: None)
137
+ folder = Path(folder).resolve()
138
+ follow = list(DEFAULT_FOLLOW_UPS if follow_ups is None else follow_ups)
139
+ steps = [(task, "build")] + [(m, STEP_MODES[min(i + 1, len(STEP_MODES) - 1)]) for i, m in enumerate(follow)]
140
+ with tempfile.TemporaryDirectory(prefix="pal-bench-") as tmp:
141
+ root = Path(tmp)
142
+ plain_dir, pal_dir = root / "plain" / folder.name, root / "pal" / folder.name
143
+ for d in (plain_dir, pal_dir):
144
+ d.mkdir(parents=True)
145
+ copy_folder(folder, d)
146
+
147
+ progress("plain: pasting the source files")
148
+ plain = PalSession(PlainClient(core_factory(), SYSTEM, max_tokens=4000), model, "bench", plain_dir, "build")
149
+ plain.pending = source_files(plain_dir) # pasted into the first message; a plain chat then resends it every time
150
+ plain_arm = _run_arm("plain", plain, steps, progress)
151
+
152
+ progress("pal: setting up the folder")
153
+ project = Project(pal_dir, home=root / "pal-home")
154
+ project.prepare(graph_in_background=False)
155
+ client = PalClient(core_factory(), None, frozenset(), network=False, home=root / "pal-engine", model_name=model, max_tokens=4000,
156
+ local=False, project=project)
157
+ pal = PalSession(client, model, "bench", pal_dir, "build")
158
+ pal.project = project
159
+ pal.brief = project.brief()
160
+ try:
161
+ pal_arm = _run_arm("pal", pal, steps, progress)
162
+ finally:
163
+ client.close()
164
+ result = BenchResult(task, model, str(folder), plain_arm, pal_arm)
165
+ save(result, home)
166
+ return result
167
+
168
+
169
+ def save(result: BenchResult, home: Path | None = None) -> Path:
170
+ out = (home or pal_home()) / "bench" / f"bench-{time.strftime('%Y%m%d-%H%M%S')}.json"
171
+ out.parent.mkdir(parents=True, exist_ok=True)
172
+ out.write_text(json.dumps(result.to_dict(), indent=1), encoding="utf-8")
173
+ return out