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.
- pal/__init__.py +29 -0
- pal/__main__.py +3 -0
- pal/access_manager/__init__.py +16 -0
- pal/access_manager/components/__init__.py +14 -0
- pal/access_manager/components/access_control.py +41 -0
- pal/access_manager/components/base.py +39 -0
- pal/access_manager/components/intervention.py +31 -0
- pal/access_manager/components/privilege_registry.py +30 -0
- pal/access_manager/pipeline.py +68 -0
- pal/access_manager/utils/__init__.py +2 -0
- pal/agent_constraints/__init__.py +3 -0
- pal/agent_constraints/components/AGENT_OUTPUT.md +13 -0
- pal/agent_constraints/components/__init__.py +3 -0
- pal/agent_constraints/components/constraints.py +22 -0
- pal/agent_constraints/pipeline.py +22 -0
- pal/agent_constraints/utils/__init__.py +2 -0
- pal/agents/__init__.py +12 -0
- pal/agents/coding.py +14 -0
- pal/agents/docs.py +15 -0
- pal/chat/__init__.py +4 -0
- pal/chat/demo.py +231 -0
- pal/chat/providers.py +23 -0
- pal/chat/service.py +591 -0
- pal/chat/services/__init__.py +26 -0
- pal/chat/services/base.py +142 -0
- pal/chat/services/data.py +142 -0
- pal/chat/services/media.py +123 -0
- pal/chat/services/web.py +60 -0
- pal/chat/sessions.py +108 -0
- pal/chat/tools.py +446 -0
- pal/chat/uploads.py +81 -0
- pal/cli.py +502 -0
- pal/compression/__init__.py +12 -0
- pal/compression/components/__init__.py +64 -0
- pal/compression/components/adverbs.py +20 -0
- pal/compression/components/hedges.py +54 -0
- pal/compression/components/interjections.py +20 -0
- pal/compression/components/minifier.py +56 -0
- pal/compression/components/output_compressor.py +13 -0
- pal/compression/components/pleonasms.py +54 -0
- pal/compression/components/punctuations.py +36 -0
- pal/compression/components/stopwords.py +62 -0
- pal/compression/history.py +346 -0
- pal/compression/pipeline.py +58 -0
- pal/compression/utils/__init__.py +30 -0
- pal/compression/utils/negations.py +18 -0
- pal/compression/utils/nltk_setup.py +20 -0
- pal/compression/utils/pronouns.py +27 -0
- pal/compression/utils/proselint_setup.py +9 -0
- pal/compression/utils/spacy_setup.py +22 -0
- pal/config/default.yaml +163 -0
- pal/config.py +504 -0
- pal/context_manager/__init__.py +7 -0
- pal/context_manager/chat_context.py +117 -0
- pal/context_manager/components/__init__.py +5 -0
- pal/context_manager/components/assembler.py +75 -0
- pal/context_manager/components/context_cache.py +45 -0
- pal/context_manager/components/kv_bridge.py +16 -0
- pal/context_manager/components/pager.py +48 -0
- pal/context_manager/components/simple_context_manager.py +56 -0
- pal/context_manager/components/turns.py +88 -0
- pal/context_manager/pipeline.py +27 -0
- pal/context_manager/utils/__init__.py +18 -0
- pal/context_manager/utils/hashing.py +5 -0
- pal/context_manager/utils/relevance.py +59 -0
- pal/context_manager/utils/storage.py +37 -0
- pal/documents/__init__.py +4 -0
- pal/documents/components/__init__.py +0 -0
- pal/documents/components/chunker.py +79 -0
- pal/documents/components/index.py +155 -0
- pal/documents/utils/__init__.py +1 -0
- pal/errors.py +40 -0
- pal/input_converters/__init__.py +8 -0
- pal/input_converters/components/__init__.py +0 -0
- pal/input_converters/components/base.py +38 -0
- pal/input_converters/components/pandoc_converter.py +87 -0
- pal/input_converters/components/pdf_converter.py +46 -0
- pal/input_converters/components/pptx_converter.py +52 -0
- pal/input_converters/components/xlsx_converter.py +38 -0
- pal/input_converters/pipeline.py +72 -0
- pal/input_converters/utils/__init__.py +1 -0
- pal/input_converters/workspace.py +56 -0
- pal/input_savers/INPUT_RULES.md +2 -0
- pal/input_savers/__init__.py +5 -0
- pal/input_savers/components/__init__.py +0 -0
- pal/input_savers/components/base.py +40 -0
- pal/input_savers/components/methods.py +41 -0
- pal/input_savers/components/verifier.py +39 -0
- pal/input_savers/pipeline.py +154 -0
- pal/input_savers/utils/__init__.py +0 -0
- pal/input_savers/utils/guards.py +94 -0
- pal/knowledge_graph/__init__.py +10 -0
- pal/knowledge_graph/components/__init__.py +0 -0
- pal/knowledge_graph/components/base.py +50 -0
- pal/knowledge_graph/components/builder.py +54 -0
- pal/knowledge_graph/components/retriever.py +100 -0
- pal/knowledge_graph/components/workspace.py +116 -0
- pal/knowledge_graph/pipeline.py +215 -0
- pal/knowledge_graph/utils/__init__.py +1 -0
- pal/latency_tracker/__init__.py +3 -0
- pal/latency_tracker/pipeline.py +86 -0
- pal/llm_scheduler/__init__.py +11 -0
- pal/llm_scheduler/api.py +137 -0
- pal/llm_scheduler/code.py +28 -0
- pal/llm_scheduler/core.py +92 -0
- pal/llm_scheduler/local.py +215 -0
- pal/llm_scheduler/pipeline.py +42 -0
- pal/llm_scheduler/types.py +92 -0
- pal/llm_wrapper/__init__.py +34 -0
- pal/llm_wrapper/adapter.py +316 -0
- pal/llm_wrapper/components/__init__.py +23 -0
- pal/llm_wrapper/components/adapter_types.py +90 -0
- pal/llm_wrapper/components/base.py +195 -0
- pal/llm_wrapper/components/features.py +104 -0
- pal/llm_wrapper/components/litellm_wrapper.py +215 -0
- pal/llm_wrapper/components/openrouter_wrapper.py +114 -0
- pal/llm_wrapper/modes.py +73 -0
- pal/llm_wrapper/pipeline.py +61 -0
- pal/llm_wrapper/utils/__init__.py +4 -0
- pal/llm_wrapper/utils/registry.py +49 -0
- pal/llm_wrapper/utils/token_counting.py +10 -0
- pal/local_llm/__init__.py +5 -0
- pal/local_llm/components/__init__.py +0 -0
- pal/local_llm/components/huggingface_local.py +217 -0
- pal/local_llm/components/local_llm.py +60 -0
- pal/logging_setup.py +18 -0
- pal/memory/__init__.py +7 -0
- pal/memory/components/__init__.py +6 -0
- pal/memory/components/lru_k.py +56 -0
- pal/memory/components/manager.py +98 -0
- pal/memory/components/note.py +54 -0
- pal/memory/components/storage_manager.py +60 -0
- pal/memory/pipeline.py +122 -0
- pal/memory/utils/__init__.py +3 -0
- pal/memory/utils/keyword_overlap.py +3 -0
- pal/memory_tracker/__init__.py +3 -0
- pal/memory_tracker/components/__init__.py +0 -0
- pal/memory_tracker/components/process.py +56 -0
- pal/memory_tracker/pipeline.py +44 -0
- pal/metrics/__init__.py +4 -0
- pal/metrics/components/__init__.py +4 -0
- pal/metrics/components/collector.py +140 -0
- pal/metrics/components/exporter.py +28 -0
- pal/metrics/pipeline.py +49 -0
- pal/metrics/utils/__init__.py +2 -0
- pal/notes_manager/__init__.py +5 -0
- pal/notes_manager/components/__init__.py +0 -0
- pal/notes_manager/components/privacy.py +14 -0
- pal/notes_manager/components/recall.py +31 -0
- pal/notes_manager/components/tidy.py +28 -0
- pal/notes_manager/pipeline.py +138 -0
- pal/orchestrator/__init__.py +26 -0
- pal/orchestrator/components/__init__.py +26 -0
- pal/orchestrator/components/agent.py +105 -0
- pal/orchestrator/components/cgroup_manager.py +209 -0
- pal/orchestrator/components/context_interrupt.py +220 -0
- pal/orchestrator/components/limits.py +9 -0
- pal/orchestrator/components/scheduler.py +69 -0
- pal/orchestrator/components/syscall_scheduler.py +200 -0
- pal/orchestrator/pipeline.py +228 -0
- pal/orchestrator/utils/__init__.py +4 -0
- pal/orchestrator/utils/device.py +164 -0
- pal/orchestrator/utils/protocol.py +32 -0
- pal/os_kernel/__init__.py +5 -0
- pal/os_kernel/filesystem.py +80 -0
- pal/os_kernel/network.py +146 -0
- pal/os_kernel/pipeline.py +175 -0
- pal/os_kernel/sysinfo.py +36 -0
- pal/paths.py +59 -0
- pal/pipeline.py +177 -0
- pal/py.typed +0 -0
- pal/redact.py +19 -0
- pal/sdk/__init__.py +33 -0
- pal/sdk/adapters.py +43 -0
- pal/sdk/agent.py +99 -0
- pal/sdk/components/__init__.py +14 -0
- pal/sdk/components/autogen_adapter.py +65 -0
- pal/sdk/components/autogen_client.py +98 -0
- pal/sdk/components/framework_adapter_base.py +69 -0
- pal/sdk/components/metagpt_adapter.py +46 -0
- pal/sdk/components/open_interpreter_adapter.py +47 -0
- pal/sdk/components/tool_catalog.py +44 -0
- pal/sdk/os_api.py +84 -0
- pal/sdk/patterns.py +129 -0
- pal/sdk/pipeline.py +240 -0
- pal/sdk/queries.py +68 -0
- pal/sdk/savers.py +88 -0
- pal/sdk/utils/__init__.py +2 -0
- pal/storage_manager/__init__.py +17 -0
- pal/storage_manager/components/__init__.py +19 -0
- pal/storage_manager/components/base.py +50 -0
- pal/storage_manager/components/local_file_backend.py +84 -0
- pal/storage_manager/components/storage_queue.py +91 -0
- pal/storage_manager/components/vector_store_backend.py +122 -0
- pal/storage_manager/pipeline.py +153 -0
- pal/storage_manager/utils/__init__.py +4 -0
- pal/storage_manager/utils/memory_adapter.py +60 -0
- pal/storage_manager/utils/sdk.py +93 -0
- pal/studio/__init__.py +0 -0
- pal/studio/builder.py +166 -0
- pal/studio/catalog.py +90 -0
- pal/studio/checks.py +129 -0
- pal/studio/clients.py +6 -0
- pal/studio/editor.py +137 -0
- pal/studio/files.py +6 -0
- pal/studio/models.py +6 -0
- pal/studio/plan.py +149 -0
- pal/studio/planner.py +205 -0
- pal/studio/policy.py +69 -0
- pal/studio/routes.py +254 -0
- pal/studio/runs.py +210 -0
- pal/task_router/__init__.py +11 -0
- pal/task_router/components/__init__.py +0 -0
- pal/task_router/components/answerer.py +37 -0
- pal/task_router/components/base.py +34 -0
- pal/task_router/components/classifier.py +40 -0
- pal/task_router/components/gates.py +23 -0
- pal/task_router/pipeline.py +61 -0
- pal/task_router/utils/__init__.py +1 -0
- pal/terminal/__init__.py +1 -0
- pal/terminal/backend.py +141 -0
- pal/terminal/bench.py +173 -0
- pal/terminal/cli.py +826 -0
- pal/terminal/clients.py +170 -0
- pal/terminal/commands.py +401 -0
- pal/terminal/decide.py +242 -0
- pal/terminal/files.py +55 -0
- pal/terminal/flow_cli.py +439 -0
- pal/terminal/hardware.py +199 -0
- pal/terminal/history.py +88 -0
- pal/terminal/keys.py +83 -0
- pal/terminal/local_models.py +261 -0
- pal/terminal/logo.py +117 -0
- pal/terminal/models.py +99 -0
- pal/terminal/policy.py +54 -0
- pal/terminal/project.py +413 -0
- pal/terminal/review.py +65 -0
- pal/terminal/security.py +239 -0
- pal/terminal/session.py +503 -0
- pal/terminal/setup.py +354 -0
- pal/terminal/syntax.py +98 -0
- pal/terminal/theme.py +37 -0
- pal/terminal/ui.py +647 -0
- pal/terminal/voice.py +137 -0
- pal/terminal/workflow.py +624 -0
- pal/token_tracker/__init__.py +3 -0
- pal/token_tracker/pipeline.py +54 -0
- pal/tokens.py +21 -0
- pal/tool_catalog.py +107 -0
- pal/tool_manager/__init__.py +17 -0
- pal/tool_manager/builtin.py +82 -0
- pal/tool_manager/components/__init__.py +19 -0
- pal/tool_manager/components/base.py +53 -0
- pal/tool_manager/components/conflict_resolver.py +27 -0
- pal/tool_manager/components/param_validator.py +41 -0
- pal/tool_manager/components/tool_call_queue.py +111 -0
- pal/tool_manager/components/tool_registry.py +60 -0
- pal/tool_manager/pipeline.py +80 -0
- pal/tool_manager/utils/__init__.py +3 -0
- pal/web/__init__.py +3 -0
- pal/web/app.py +276 -0
- pal/web/static/editor.js +219 -0
- pal/web/static/fonts/LICENSE-Inter.txt +3 -0
- pal/web/static/fonts/inter-400.woff2 +0 -0
- pal/web/static/fonts/inter-500.woff2 +0 -0
- pal/web/static/fonts/inter-600.woff2 +0 -0
- pal/web/static/fonts/inter-700.woff2 +0 -0
- pal/web/static/inter.css +5 -0
- pal/web/static/pal-logo-256.png +0 -0
- pal/web/static/pal-logo-32.png +0 -0
- pal/web/static/pal-logo-64.png +0 -0
- pal/web/static/studio.css +306 -0
- pal/web/static/studio.js +533 -0
- pal/web/static/voice.js +76 -0
- pal/web/templates/index.html +368 -0
- pal/web/templates/studio.html +120 -0
- pal_code-0.1.0.dist-info/METADATA +379 -0
- pal_code-0.1.0.dist-info/RECORD +282 -0
- pal_code-0.1.0.dist-info/WHEEL +5 -0
- pal_code-0.1.0.dist-info/entry_points.txt +2 -0
- pal_code-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|