Initial ForgeLM import

This commit is contained in:
Evandro Fonseca Junior committed 2026-03-29 19:10:57 -03:00
commit a392b9dc60
4296 files changed
+1212675

No files matched your search

+119
View File
@@ -0,0 +1,119 @@
#!/usr/bin/env python3
import argparse
import json
import math
import statistics
import sys
import time
import urllib.error
import urllib.request
def percentile(sorted_values, pct):
if not sorted_values:
return 0.0
if len(sorted_values) == 1:
return float(sorted_values[0])
rank = (len(sorted_values) - 1) * pct
low = math.floor(rank)
high = math.ceil(rank)
if low == high:
return float(sorted_values[low])
weight = rank - low
return float(sorted_values[low] * (1.0 - weight) + sorted_values[high] * weight)
def wait_for_health(base_url, timeout_s, server_log):
deadline = time.time() + timeout_s
last_error = "server did not become healthy"
while time.time() < deadline:
try:
with urllib.request.urlopen(base_url + "/health", timeout=2) as response:
if response.status == 200:
return
except Exception as exc: # noqa: BLE001
last_error = str(exc)
time.sleep(0.5)
print("[ERROR] health check failed: {}".format(last_error), file=sys.stderr)
if server_log:
try:
with open(server_log, "r", encoding="utf-8", errors="replace") as fh:
lines = fh.readlines()[-40:]
print("[ERROR] tail of server log:", file=sys.stderr)
for line in lines:
print(line.rstrip(), file=sys.stderr)
except OSError as exc: # noqa: BLE001
print("[ERROR] failed to read server log: {}".format(exc), file=sys.stderr)
sys.exit(1)
def build_payload(model_id):
return {
"model": model_id,
"stream": False,
"temperature": 0.0,
"top_p": 1.0,
"max_completion_tokens": 48,
"messages": [
{
"role": "user",
"content": "Write a compact Rust function that parses a comma-separated list of u32 values and returns a Vec<u32> while ignoring empty segments.",
}
],
}
def run_once(base_url, model_id):
body = json.dumps(build_payload(model_id)).encode("utf-8")
req = urllib.request.Request(
base_url + "/v1/chat/completions",
data=body,
headers={"content-type": "application/json"},
method="POST",
)
started = time.perf_counter()
with urllib.request.urlopen(req, timeout=180) as response:
payload = json.loads(response.read().decode("utf-8"))
elapsed_ms = (time.perf_counter() - started) * 1000.0
usage = payload.get("usage") or {}
return elapsed_ms, float(usage.get("tokens_per_second") or 0.0)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--base-url", required=True)
parser.add_argument("--model-id", required=True)
parser.add_argument("--server-log", default="")
parser.add_argument("--warmups", type=int, default=1)
parser.add_argument("--samples", type=int, default=7)
parser.add_argument("--health-timeout", type=float, default=120.0)
args = parser.parse_args()
wait_for_health(args.base_url, args.health_timeout, args.server_log)
for _ in range(args.warmups):
run_once(args.base_url, args.model_id)
latencies = []
tps_values = []
for _ in range(args.samples):
latency_ms, tps = run_once(args.base_url, args.model_id)
latencies.append(latency_ms)
tps_values.append(tps)
latencies_sorted = sorted(latencies)
p50_ms = percentile(latencies_sorted, 0.50)
p95_ms = percentile(latencies_sorted, 0.95)
mean_ms = statistics.fmean(latencies)
completion_tps_mean = statistics.fmean(tps_values) if tps_values else 0.0
print("METRIC p95_ms={:.3f}".format(p95_ms))
print("METRIC p50_ms={:.3f}".format(p50_ms))
print("METRIC mean_ms={:.3f}".format(mean_ms))
print("METRIC completion_tps_mean={:.3f}".format(completion_tps_mean))
print("METRIC success_count={}".format(len(latencies)))
if __name__ == "__main__":
main()
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
import argparse
import re
import subprocess
import sys
from pathlib import Path
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", required=True)
args = parser.parse_args()
root = Path(args.root)
command = [
"cargo",
"test",
"-p",
"localllm-cli",
"cli_surface_",
"--",
"--ignored",
"--nocapture",
]
completed = subprocess.run(
command,
cwd=root,
text=True,
capture_output=True,
)
output = (completed.stdout or "") + (completed.stderr or "")
sys.stdout.write(output)
passed_match = re.search(
r"test result: ok\. (\d+) passed; (\d+) failed; (\d+) ignored;", output
)
failed_match = re.search(
r"test result: FAILED\. (\d+) passed; (\d+) failed; (\d+) ignored;", output
)
passed_cases = 0
failed_cases = 0
total_cases = 0
if passed_match:
passed_cases = int(passed_match.group(1))
failed_cases = int(passed_match.group(2))
total_cases = passed_cases + failed_cases
elif failed_match:
passed_cases = int(failed_match.group(1))
failed_cases = int(failed_match.group(2))
total_cases = passed_cases + failed_cases
if completed.returncode != 0 and total_cases == 0:
print("METRIC cli_surface_score=0.0")
print("METRIC passed_cases=0")
print("METRIC failed_cases=0")
print("METRIC total_cases=0")
return completed.returncode
score = 0.0 if total_cases == 0 else round((passed_cases / total_cases) * 100.0, 3)
print(f"METRIC cli_surface_score={score}")
print(f"METRIC passed_cases={passed_cases}")
print(f"METRIC failed_cases={failed_cases}")
print(f"METRIC total_cases={total_cases}")
return 0 if total_cases > 0 else completed.returncode
if __name__ == "__main__":
raise SystemExit(main())
+175
View File
@@ -0,0 +1,175 @@
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path
def run(command: list[str], cwd: Path) -> bool:
print(f"$ {' '.join(command)}")
result = subprocess.run(command, cwd=cwd)
return result.returncode == 0
def discover_model(root: Path) -> Path | None:
explicit = os.environ.get("LOCALLLM_TEST_MODEL")
if explicit:
path = Path(explicit)
return path if path.exists() else None
models_dir = root / "models"
candidates = sorted(models_dir.glob("*.gguf"))
return candidates[0] if candidates else None
def wait_json(url: str, timeout_secs: float) -> dict | None:
deadline = time.time() + timeout_secs
while time.time() < deadline:
try:
with urllib.request.urlopen(url, timeout=5) as response:
return json.load(response)
except Exception:
time.sleep(1)
return None
def post_json(url: str, payload: dict, timeout_secs: float) -> dict | None:
data = json.dumps(payload).encode("utf-8")
request = urllib.request.Request(
url,
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request, timeout=timeout_secs) as response:
return json.load(response)
def run_live_completion(root: Path) -> bool:
model = discover_model(root)
if model is None:
print("SKIP live completion: no .gguf model found")
return False
stdout_path = root / "experiments" / "localllm-live-stdout.log"
stderr_path = root / "experiments" / "localllm-live-stderr.log"
stdout_path.parent.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env.update(
{
"LOCALLLM_MODEL": str(model),
"LOCALLLM_ADDR": "127.0.0.1:18083",
"LOCALLLM_BATCH": "128",
"LOCALLLM_UBATCH": "128",
"LOCALLLM_CTX": "16384",
}
)
stdout_file = stdout_path.open("w", encoding="utf-8")
stderr_file = stderr_path.open("w", encoding="utf-8")
process: subprocess.Popen[str] | None = None
try:
process = subprocess.Popen(
[str(root / "target" / "debug" / "localllm.exe")],
cwd=root,
env=env,
stdout=stdout_file,
stderr=stderr_file,
text=True,
)
health = wait_json("http://127.0.0.1:18083/health", timeout_secs=90)
if not health or health.get("status") != "ok":
return False
models = wait_json("http://127.0.0.1:18083/v1/models", timeout_secs=15)
if not models or not models.get("data"):
return False
model_id = models["data"][0]["id"]
prompt = (
"token " * 220
) + "Explain in one sentence why chunked prompt prefill matters."
response = post_json(
"http://127.0.0.1:18083/v1/chat/completions",
{
"model": model_id,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 128,
},
timeout_secs=180,
)
return bool(response and response.get("choices"))
except (
OSError,
urllib.error.URLError,
urllib.error.HTTPError,
TimeoutError,
json.JSONDecodeError,
):
return False
finally:
if process is not None:
process.terminate()
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
process.kill()
process.wait(timeout=10)
stdout_file.close()
stderr_file.close()
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", required=True)
args = parser.parse_args()
root = Path(args.root)
checks = {
"prefill_plan_passed": run(
[
"cargo",
"test",
"-p",
"inference-core",
"prefill_plan_",
],
root,
),
"cargo_check_passed": run(
["cargo", "check", "-p", "inference-core"],
root,
),
"server_build_passed": run(
["cargo", "build", "-p", "inference-server"],
root,
),
}
checks["live_completion_passed"] = checks[
"server_build_passed"
] and run_live_completion(root)
passed = sum(1 for ok in checks.values() if ok)
total = len(checks)
score = (passed / total) * 100.0
print(f"METRIC prefill_batch_assert_score={score:.1f}")
for name, ok in checks.items():
print(f"METRIC {name}={1 if ok else 0}")
return 0 if passed == total else 1
if __name__ == "__main__":
sys.exit(main())
+141
View File
@@ -0,0 +1,141 @@
#!/usr/bin/env python3
import argparse
import os
import subprocess
import sys
from pathlib import Path
def run_command(command: list[str], cwd: Path) -> tuple[int, str]:
completed = subprocess.run(command, cwd=cwd, text=True, capture_output=True)
output = (completed.stdout or "") + (completed.stderr or "")
if output:
sys.stdout.write(output)
if not output.endswith("\n"):
sys.stdout.write("\n")
return completed.returncode, output
def command_name(name: str) -> str:
if os.name == "nt" and name == "npm":
return "npm.cmd"
return name
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", required=True)
args = parser.parse_args()
root = Path(args.root)
tauri_app = root / "tauri-app"
tauri_backend = tauri_app / "src-tauri"
unit_command = [
command_name("npm"),
"test",
"--",
"src/features/runtime/runtime-utils.test.ts",
"src/features/runtime/components/RuntimeSurface.test.tsx",
]
saved_startup_command = [
command_name("npm"),
"test",
"--",
"src/features/runtime/components/RuntimeSurface.test.tsx",
"-t",
"covers the saved local startup lifecycle from stopped to starting to ready",
]
local_onboarding_command = [
command_name("npm"),
"test",
"--",
"src/features/runtime/components/RuntimeSurface.test.tsx",
"-t",
"supports local onboarding from browsing a models folder through save and start",
]
local_mode_cta_command = [
command_name("npm"),
"test",
"--",
"src/features/runtime/components/RuntimeSurface.test.tsx",
"-t",
"keeps the local primary CTA visible while switching from remote to local mode",
]
health_revalidation_surface_command = [
command_name("npm"),
"test",
"--",
"src/features/runtime/components/RuntimeSurface.test.tsx",
"-t",
"surfaces degraded local runtime state after periodic health revalidation fails",
]
build_command = [command_name("npm"), "run", "build"]
cargo_check_command = [
command_name("cargo"),
"check",
"--manifest-path",
str(tauri_backend / "Cargo.toml"),
]
health_revalidation_backend_command = [
command_name("cargo"),
"test",
"--manifest-path",
str(tauri_backend / "Cargo.toml"),
"runtime::tests::runtime_revalidation_marks_unhealthy_local_process_as_degraded",
"--",
"--exact",
]
unit_code, _ = run_command(unit_command, tauri_app)
saved_startup_code, _ = run_command(saved_startup_command, tauri_app)
local_onboarding_code, _ = run_command(local_onboarding_command, tauri_app)
local_mode_cta_code, _ = run_command(local_mode_cta_command, tauri_app)
health_revalidation_surface_code, _ = run_command(
health_revalidation_surface_command, tauri_app
)
build_code, _ = run_command(build_command, tauri_app)
cargo_check_code, _ = run_command(cargo_check_command, root)
health_revalidation_backend_code, _ = run_command(
health_revalidation_backend_command, root
)
unit_passed = 1 if unit_code == 0 else 0
saved_startup_flow_passed = 1 if saved_startup_code == 0 else 0
local_onboarding_flow_passed = 1 if local_onboarding_code == 0 else 0
local_mode_cta_passed = 1 if local_mode_cta_code == 0 else 0
health_revalidation_passed = (
1
if health_revalidation_surface_code == 0
and health_revalidation_backend_code == 0
else 0
)
build_passed = 1 if build_code == 0 else 0
cargo_check_passed = 1 if cargo_check_code == 0 else 0
total_checks = 7
passed_checks = (
unit_passed
+ saved_startup_flow_passed
+ local_onboarding_flow_passed
+ local_mode_cta_passed
+ health_revalidation_passed
+ build_passed
+ cargo_check_passed
)
score = round((passed_checks / total_checks) * 100.0, 3)
print(f"METRIC runtime_lifecycle_score={score}")
print(f"METRIC unit_passed={unit_passed}")
print(f"METRIC local_mode_cta_passed={local_mode_cta_passed}")
print(f"METRIC saved_startup_flow_passed={saved_startup_flow_passed}")
print(f"METRIC local_onboarding_flow_passed={local_onboarding_flow_passed}")
print(f"METRIC health_revalidation_passed={health_revalidation_passed}")
print(f"METRIC build_passed={build_passed}")
print(f"METRIC cargo_check_passed={cargo_check_passed}")
return 0 if passed_checks == total_checks else 1
if __name__ == "__main__":
raise SystemExit(main())
+739
View File
@@ -0,0 +1,739 @@
ggml_cuda_init: found 1 CUDA devices (Total VRAM: 8191 MiB):
Device 0: NVIDIA GeForce RTX 3070, compute capability 8.6, VMM: yes, VRAM: 8191 MiB
load_backend: loaded CUDA backend from D:\Documents\Github\ai-backend-studio\target\debug\ggml-cuda.dll
load_backend: loaded RPC backend from D:\Documents\Github\ai-backend-studio\target\debug\ggml-rpc.dll
load_backend: loaded CPU backend from D:\Documents\Github\ai-backend-studio\target\debug\ggml-cpu-haswell.dll
llama_model_load_from_file_impl: using device CUDA0 (NVIDIA GeForce RTX 3070) (0000:2b:00.0) - 7113 MiB free
llama_model_loader: loaded meta data with 53 key-value pairs and 426 tensors from D:\Documents\Github\ai-backend-studio\models\Qwen3.5-4B-Abliterated-Claude-4.6-Opus-Reasoning-Distilled.i1-Q6_K.gguf (version GGUF V3 (latest))
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
llama_model_loader: - kv 0: general.architecture str = qwen35
llama_model_loader: - kv 1: general.type str = model
llama_model_loader: - kv 2: general.name str = Qwen3.5-4B-Abliterated-Claude-4.6-Opu...
llama_model_loader: - kv 3: general.version str = 4.6
llama_model_loader: - kv 4: general.finetune str = Abliterated-Claude-Opus-Reasoning-Dis...
llama_model_loader: - kv 5: general.basename str = Qwen3.5
llama_model_loader: - kv 6: general.size_label str = 4B
llama_model_loader: - kv 7: general.license str = other
llama_model_loader: - kv 8: general.base_model.count u32 = 1
llama_model_loader: - kv 9: general.base_model.0.name str = Qwen3.5 4B Claude 4.6 Opus Reasoning ...
llama_model_loader: - kv 10: general.base_model.0.version str = 4.6
llama_model_loader: - kv 11: general.base_model.0.organization str = Jackrong
llama_model_loader: - kv 12: general.base_model.0.repo_url str = https://huggingface.co/Jackrong/Qwen3...
llama_model_loader: - kv 13: general.tags arr[str,5] = ["abliterated", "de-censored", "reaso...
llama_model_loader: - kv 14: qwen35.block_count u32 = 32
llama_model_loader: - kv 15: qwen35.context_length u32 = 262144
llama_model_loader: - kv 16: qwen35.embedding_length u32 = 2560
llama_model_loader: - kv 17: qwen35.feed_forward_length u32 = 9216
llama_model_loader: - kv 18: qwen35.attention.head_count u32 = 16
llama_model_loader: - kv 19: qwen35.attention.head_count_kv u32 = 4
llama_model_loader: - kv 20: qwen35.rope.dimension_sections arr[i32,4] = [11, 11, 10, 0]
llama_model_loader: - kv 21: qwen35.rope.freq_base f32 = 10000000.000000
llama_model_loader: - kv 22: qwen35.attention.layer_norm_rms_epsilon f32 = 0.000001
llama_model_loader: - kv 23: qwen35.attention.key_length u32 = 256
llama_model_loader: - kv 24: qwen35.attention.value_length u32 = 256
llama_model_loader: - kv 25: qwen35.ssm.conv_kernel u32 = 4
llama_model_loader: - kv 26: qwen35.ssm.state_size u32 = 128
llama_model_loader: - kv 27: qwen35.ssm.group_count u32 = 16
llama_model_loader: - kv 28: qwen35.ssm.time_step_rank u32 = 32
llama_model_loader: - kv 29: qwen35.ssm.inner_size u32 = 4096
llama_model_loader: - kv 30: qwen35.full_attention_interval u32 = 4
llama_model_loader: - kv 31: qwen35.rope.dimension_count u32 = 64
llama_model_loader: - kv 32: tokenizer.ggml.model str = gpt2
llama_model_loader: - kv 33: tokenizer.ggml.pre str = qwen35
llama_model_loader: - kv 34: tokenizer.ggml.tokens arr[str,248320] = ["!", "\"", "#", "$", "%", "&", "'", ...
llama_model_loader: - kv 35: tokenizer.ggml.token_type arr[i32,248320] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
llama_model_loader: - kv 36: tokenizer.ggml.merges arr[str,247587] = ["Ġ Ġ", "ĠĠ ĠĠ", "i n", "Ġ t",...
llama_model_loader: - kv 37: tokenizer.ggml.eos_token_id u32 = 248046
llama_model_loader: - kv 38: tokenizer.ggml.padding_token_id u32 = 248055
llama_model_loader: - kv 39: tokenizer.chat_template str = {%- if tools %}\n {{- '<|im_start|>...
llama_model_loader: - kv 40: general.quantization_version u32 = 2
llama_model_loader: - kv 41: general.file_type u32 = 18
llama_model_loader: - kv 42: general.url str = https://huggingface.co/mradermacher/Q...
llama_model_loader: - kv 43: mradermacher.quantize_version str = 2
llama_model_loader: - kv 44: mradermacher.quantized_by str = mradermacher
llama_model_loader: - kv 45: mradermacher.quantized_at str = 2026-03-10T09:59:38+01:00
llama_model_loader: - kv 46: mradermacher.quantized_on str = nico1
llama_model_loader: - kv 47: general.source.url str = https://huggingface.co/Abhiray/Qwen3....
llama_model_loader: - kv 48: mradermacher.convert_type str = hf
llama_model_loader: - kv 49: quantize.imatrix.file str = Qwen3.5-4B-Abliterated-Claude-4.6-Opu...
llama_model_loader: - kv 50: quantize.imatrix.dataset str = imatrix-training-full-3
llama_model_loader: - kv 51: quantize.imatrix.entries_count u32 = 248
llama_model_loader: - kv 52: quantize.imatrix.chunks_count u32 = 319
llama_model_loader: - type f32: 177 tensors
llama_model_loader: - type q6_K: 249 tensors
print_info: file format = GGUF V3 (latest)
print_info: file type = Q6_K
print_info: file size = 3.22 GiB (6.57 BPW)
init_tokenizer: initializing tokenizer for type 2
load: 0 unused tokens
load: control token: 248062 '<|fim_suffix|>' is not marked as EOG
load: control token: 248072 '<tts_pad>' is not marked as EOG
load: control token: 248050 '<|box_end|>' is not marked as EOG
load: control token: 248048 '<|object_ref_end|>' is not marked as EOG
load: control token: 248055 '<|vision_pad|>' is not marked as EOG
load: control token: 248060 '<|fim_prefix|>' is not marked as EOG
load: control token: 248049 '<|box_start|>' is not marked as EOG
load: control token: 248073 '<tts_text_bos>' is not marked as EOG
load: control token: 248052 '<|quad_end|>' is not marked as EOG
load: control token: 248054 '<|vision_end|>' is not marked as EOG
load: control token: 248075 '<tts_text_bos_single>' is not marked as EOG
load: control token: 248056 '<|image_pad|>' is not marked as EOG
load: control token: 248071 '<|audio_end|>' is not marked as EOG
load: control token: 248045 '<|im_start|>' is not marked as EOG
load: control token: 248047 '<|object_ref_start|>' is not marked as EOG
load: control token: 248051 '<|quad_start|>' is not marked as EOG
load: control token: 248053 '<|vision_start|>' is not marked as EOG
load: control token: 248057 '<|video_pad|>' is not marked as EOG
load: control token: 248061 '<|fim_middle|>' is not marked as EOG
load: control token: 248070 '<|audio_start|>' is not marked as EOG
load: control token: 248074 '<tts_text_eod>' is not marked as EOG
load: control token: 248076 '<|audio_pad|>' is not marked as EOG
load: printing all EOG tokens:
load: - 248044 ('<|endoftext|>')
load: - 248046 ('<|im_end|>')
load: - 248063 ('<|fim_pad|>')
load: - 248064 ('<|repo_name|>')
load: - 248065 ('<|file_sep|>')
load: special tokens cache size = 33
load: token to piece cache size = 1.7581 MB
print_info: arch = qwen35
print_info: vocab_only = 0
print_info: no_alloc = 0
print_info: n_ctx_train = 262144
print_info: n_embd = 2560
print_info: n_embd_inp = 2560
print_info: n_layer = 32
print_info: n_head = 16
print_info: n_head_kv = 4
print_info: n_rot = 64
print_info: n_swa = 0
print_info: is_swa_any = 0
print_info: n_embd_head_k = 256
print_info: n_embd_head_v = 256
print_info: n_gqa = 4
print_info: n_embd_k_gqa = 1024
print_info: n_embd_v_gqa = 1024
print_info: f_norm_eps = 0.0e+00
print_info: f_norm_rms_eps = 1.0e-06
print_info: f_clamp_kqv = 0.0e+00
print_info: f_max_alibi_bias = 0.0e+00
print_info: f_logit_scale = 0.0e+00
print_info: f_attn_scale = 0.0e+00
print_info: n_ff = 9216
print_info: n_expert = 0
print_info: n_expert_used = 0
print_info: n_expert_groups = 0
print_info: n_group_used = 0
print_info: causal attn = 1
print_info: pooling type = -1
print_info: rope type = 40
print_info: rope scaling = linear
print_info: freq_base_train = 10000000.0
print_info: freq_scale_train = 1
print_info: n_ctx_orig_yarn = 262144
print_info: rope_yarn_log_mul = 0.0000
print_info: rope_finetuned = unknown
print_info: mrope sections = [11, 11, 10, 0]
print_info: ssm_d_conv = 4
print_info: ssm_d_inner = 4096
print_info: ssm_d_state = 128
print_info: ssm_dt_rank = 32
print_info: ssm_n_group = 16
print_info: ssm_dt_b_c_rms = 0
print_info: model type = 4B
print_info: model params = 4.21 B
print_info: general.name = Qwen3.5-4B-Abliterated-Claude-4.6-Opus-Reasoning-Distilled
print_info: vocab type = BPE
print_info: n_vocab = 248320
print_info: n_merges = 247587
print_info: BOS token = 11 ','
print_info: EOS token = 248046 '<|im_end|>'
print_info: EOT token = 248046 '<|im_end|>'
print_info: PAD token = 248055 '<|vision_pad|>'
print_info: LF token = 198 'Ċ'
print_info: FIM PRE token = 248060 '<|fim_prefix|>'
print_info: FIM SUF token = 248062 '<|fim_suffix|>'
print_info: FIM MID token = 248061 '<|fim_middle|>'
print_info: FIM PAD token = 248063 '<|fim_pad|>'
print_info: FIM REP token = 248064 '<|repo_name|>'
print_info: FIM SEP token = 248065 '<|file_sep|>'
print_info: EOG token = 248044 '<|endoftext|>'
print_info: EOG token = 248046 '<|im_end|>'
print_info: EOG token = 248063 '<|fim_pad|>'
print_info: EOG token = 248064 '<|repo_name|>'
print_info: EOG token = 248065 '<|file_sep|>'
print_info: max token length = 256
load_tensors: loading model tensors, this can take a while... (mmap = false, direct_io = false)
load_tensors: layer 0 assigned to device CUDA0, is_swa = 0
load_tensors: layer 1 assigned to device CUDA0, is_swa = 0
load_tensors: layer 2 assigned to device CUDA0, is_swa = 0
load_tensors: layer 3 assigned to device CUDA0, is_swa = 0
load_tensors: layer 4 assigned to device CUDA0, is_swa = 0
load_tensors: layer 5 assigned to device CUDA0, is_swa = 0
load_tensors: layer 6 assigned to device CUDA0, is_swa = 0
load_tensors: layer 7 assigned to device CUDA0, is_swa = 0
load_tensors: layer 8 assigned to device CUDA0, is_swa = 0
load_tensors: layer 9 assigned to device CUDA0, is_swa = 0
load_tensors: layer 10 assigned to device CUDA0, is_swa = 0
load_tensors: layer 11 assigned to device CUDA0, is_swa = 0
load_tensors: layer 12 assigned to device CUDA0, is_swa = 0
load_tensors: layer 13 assigned to device CUDA0, is_swa = 0
load_tensors: layer 14 assigned to device CUDA0, is_swa = 0
load_tensors: layer 15 assigned to device CUDA0, is_swa = 0
load_tensors: layer 16 assigned to device CUDA0, is_swa = 0
load_tensors: layer 17 assigned to device CUDA0, is_swa = 0
load_tensors: layer 18 assigned to device CUDA0, is_swa = 0
load_tensors: layer 19 assigned to device CUDA0, is_swa = 0
load_tensors: layer 20 assigned to device CUDA0, is_swa = 0
load_tensors: layer 21 assigned to device CUDA0, is_swa = 0
load_tensors: layer 22 assigned to device CUDA0, is_swa = 0
load_tensors: layer 23 assigned to device CUDA0, is_swa = 0
load_tensors: layer 24 assigned to device CUDA0, is_swa = 0
load_tensors: layer 25 assigned to device CUDA0, is_swa = 0
load_tensors: layer 26 assigned to device CUDA0, is_swa = 0
load_tensors: layer 27 assigned to device CUDA0, is_swa = 0
load_tensors: layer 28 assigned to device CUDA0, is_swa = 0
load_tensors: layer 29 assigned to device CUDA0, is_swa = 0
load_tensors: layer 30 assigned to device CUDA0, is_swa = 0
load_tensors: layer 31 assigned to device CUDA0, is_swa = 0
load_tensors: layer 32 assigned to device CUDA0, is_swa = 0
create_tensor: loading tensor token_embd.weight
create_tensor: loading tensor output_norm.weight
create_tensor: loading tensor token_embd.weight
create_tensor: loading tensor blk.0.attn_norm.weight
create_tensor: loading tensor blk.0.post_attention_norm.weight
create_tensor: loading tensor blk.0.attn_qkv.weight
create_tensor: loading tensor blk.0.attn_gate.weight
create_tensor: loading tensor blk.0.ssm_conv1d.weight
create_tensor: loading tensor blk.0.ssm_dt.bias
create_tensor: loading tensor blk.0.ssm_a
create_tensor: loading tensor blk.0.ssm_beta.weight
create_tensor: loading tensor blk.0.ssm_alpha.weight
create_tensor: loading tensor blk.0.ssm_norm.weight
create_tensor: loading tensor blk.0.ssm_out.weight
create_tensor: loading tensor blk.0.ffn_gate.weight
create_tensor: loading tensor blk.0.ffn_down.weight
create_tensor: loading tensor blk.0.ffn_up.weight
create_tensor: loading tensor blk.1.attn_norm.weight
create_tensor: loading tensor blk.1.post_attention_norm.weight
create_tensor: loading tensor blk.1.attn_qkv.weight
create_tensor: loading tensor blk.1.attn_gate.weight
create_tensor: loading tensor blk.1.ssm_conv1d.weight
create_tensor: loading tensor blk.1.ssm_dt.bias
create_tensor: loading tensor blk.1.ssm_a
create_tensor: loading tensor blk.1.ssm_beta.weight
create_tensor: loading tensor blk.1.ssm_alpha.weight
create_tensor: loading tensor blk.1.ssm_norm.weight
create_tensor: loading tensor blk.1.ssm_out.weight
create_tensor: loading tensor blk.1.ffn_gate.weight
create_tensor: loading tensor blk.1.ffn_down.weight
create_tensor: loading tensor blk.1.ffn_up.weight
create_tensor: loading tensor blk.2.attn_norm.weight
create_tensor: loading tensor blk.2.post_attention_norm.weight
create_tensor: loading tensor blk.2.attn_qkv.weight
create_tensor: loading tensor blk.2.attn_gate.weight
create_tensor: loading tensor blk.2.ssm_conv1d.weight
create_tensor: loading tensor blk.2.ssm_dt.bias
create_tensor: loading tensor blk.2.ssm_a
create_tensor: loading tensor blk.2.ssm_beta.weight
create_tensor: loading tensor blk.2.ssm_alpha.weight
create_tensor: loading tensor blk.2.ssm_norm.weight
create_tensor: loading tensor blk.2.ssm_out.weight
create_tensor: loading tensor blk.2.ffn_gate.weight
create_tensor: loading tensor blk.2.ffn_down.weight
create_tensor: loading tensor blk.2.ffn_up.weight
create_tensor: loading tensor blk.3.attn_norm.weight
create_tensor: loading tensor blk.3.post_attention_norm.weight
create_tensor: loading tensor blk.3.attn_q.weight
create_tensor: loading tensor blk.3.attn_k.weight
create_tensor: loading tensor blk.3.attn_v.weight
create_tensor: loading tensor blk.3.attn_output.weight
create_tensor: loading tensor blk.3.attn_q_norm.weight
create_tensor: loading tensor blk.3.attn_k_norm.weight
create_tensor: loading tensor blk.3.ffn_gate.weight
create_tensor: loading tensor blk.3.ffn_down.weight
create_tensor: loading tensor blk.3.ffn_up.weight
create_tensor: loading tensor blk.4.attn_norm.weight
create_tensor: loading tensor blk.4.post_attention_norm.weight
create_tensor: loading tensor blk.4.attn_qkv.weight
create_tensor: loading tensor blk.4.attn_gate.weight
create_tensor: loading tensor blk.4.ssm_conv1d.weight
create_tensor: loading tensor blk.4.ssm_dt.bias
create_tensor: loading tensor blk.4.ssm_a
create_tensor: loading tensor blk.4.ssm_beta.weight
create_tensor: loading tensor blk.4.ssm_alpha.weight
create_tensor: loading tensor blk.4.ssm_norm.weight
create_tensor: loading tensor blk.4.ssm_out.weight
create_tensor: loading tensor blk.4.ffn_gate.weight
create_tensor: loading tensor blk.4.ffn_down.weight
create_tensor: loading tensor blk.4.ffn_up.weight
create_tensor: loading tensor blk.5.attn_norm.weight
create_tensor: loading tensor blk.5.post_attention_norm.weight
create_tensor: loading tensor blk.5.attn_qkv.weight
create_tensor: loading tensor blk.5.attn_gate.weight
create_tensor: loading tensor blk.5.ssm_conv1d.weight
create_tensor: loading tensor blk.5.ssm_dt.bias
create_tensor: loading tensor blk.5.ssm_a
create_tensor: loading tensor blk.5.ssm_beta.weight
create_tensor: loading tensor blk.5.ssm_alpha.weight
create_tensor: loading tensor blk.5.ssm_norm.weight
create_tensor: loading tensor blk.5.ssm_out.weight
create_tensor: loading tensor blk.5.ffn_gate.weight
create_tensor: loading tensor blk.5.ffn_down.weight
create_tensor: loading tensor blk.5.ffn_up.weight
create_tensor: loading tensor blk.6.attn_norm.weight
create_tensor: loading tensor blk.6.post_attention_norm.weight
create_tensor: loading tensor blk.6.attn_qkv.weight
create_tensor: loading tensor blk.6.attn_gate.weight
create_tensor: loading tensor blk.6.ssm_conv1d.weight
create_tensor: loading tensor blk.6.ssm_dt.bias
create_tensor: loading tensor blk.6.ssm_a
create_tensor: loading tensor blk.6.ssm_beta.weight
create_tensor: loading tensor blk.6.ssm_alpha.weight
create_tensor: loading tensor blk.6.ssm_norm.weight
create_tensor: loading tensor blk.6.ssm_out.weight
create_tensor: loading tensor blk.6.ffn_gate.weight
create_tensor: loading tensor blk.6.ffn_down.weight
create_tensor: loading tensor blk.6.ffn_up.weight
create_tensor: loading tensor blk.7.attn_norm.weight
create_tensor: loading tensor blk.7.post_attention_norm.weight
create_tensor: loading tensor blk.7.attn_q.weight
create_tensor: loading tensor blk.7.attn_k.weight
create_tensor: loading tensor blk.7.attn_v.weight
create_tensor: loading tensor blk.7.attn_output.weight
create_tensor: loading tensor blk.7.attn_q_norm.weight
create_tensor: loading tensor blk.7.attn_k_norm.weight
create_tensor: loading tensor blk.7.ffn_gate.weight
create_tensor: loading tensor blk.7.ffn_down.weight
create_tensor: loading tensor blk.7.ffn_up.weight
create_tensor: loading tensor blk.8.attn_norm.weight
create_tensor: loading tensor blk.8.post_attention_norm.weight
create_tensor: loading tensor blk.8.attn_qkv.weight
create_tensor: loading tensor blk.8.attn_gate.weight
create_tensor: loading tensor blk.8.ssm_conv1d.weight
create_tensor: loading tensor blk.8.ssm_dt.bias
create_tensor: loading tensor blk.8.ssm_a
create_tensor: loading tensor blk.8.ssm_beta.weight
create_tensor: loading tensor blk.8.ssm_alpha.weight
create_tensor: loading tensor blk.8.ssm_norm.weight
create_tensor: loading tensor blk.8.ssm_out.weight
create_tensor: loading tensor blk.8.ffn_gate.weight
create_tensor: loading tensor blk.8.ffn_down.weight
create_tensor: loading tensor blk.8.ffn_up.weight
create_tensor: loading tensor blk.9.attn_norm.weight
create_tensor: loading tensor blk.9.post_attention_norm.weight
create_tensor: loading tensor blk.9.attn_qkv.weight
create_tensor: loading tensor blk.9.attn_gate.weight
create_tensor: loading tensor blk.9.ssm_conv1d.weight
create_tensor: loading tensor blk.9.ssm_dt.bias
create_tensor: loading tensor blk.9.ssm_a
create_tensor: loading tensor blk.9.ssm_beta.weight
create_tensor: loading tensor blk.9.ssm_alpha.weight
create_tensor: loading tensor blk.9.ssm_norm.weight
create_tensor: loading tensor blk.9.ssm_out.weight
create_tensor: loading tensor blk.9.ffn_gate.weight
create_tensor: loading tensor blk.9.ffn_down.weight
create_tensor: loading tensor blk.9.ffn_up.weight
create_tensor: loading tensor blk.10.attn_norm.weight
create_tensor: loading tensor blk.10.post_attention_norm.weight
create_tensor: loading tensor blk.10.attn_qkv.weight
create_tensor: loading tensor blk.10.attn_gate.weight
create_tensor: loading tensor blk.10.ssm_conv1d.weight
create_tensor: loading tensor blk.10.ssm_dt.bias
create_tensor: loading tensor blk.10.ssm_a
create_tensor: loading tensor blk.10.ssm_beta.weight
create_tensor: loading tensor blk.10.ssm_alpha.weight
create_tensor: loading tensor blk.10.ssm_norm.weight
create_tensor: loading tensor blk.10.ssm_out.weight
create_tensor: loading tensor blk.10.ffn_gate.weight
create_tensor: loading tensor blk.10.ffn_down.weight
create_tensor: loading tensor blk.10.ffn_up.weight
create_tensor: loading tensor blk.11.attn_norm.weight
create_tensor: loading tensor blk.11.post_attention_norm.weight
create_tensor: loading tensor blk.11.attn_q.weight
create_tensor: loading tensor blk.11.attn_k.weight
create_tensor: loading tensor blk.11.attn_v.weight
create_tensor: loading tensor blk.11.attn_output.weight
create_tensor: loading tensor blk.11.attn_q_norm.weight
create_tensor: loading tensor blk.11.attn_k_norm.weight
create_tensor: loading tensor blk.11.ffn_gate.weight
create_tensor: loading tensor blk.11.ffn_down.weight
create_tensor: loading tensor blk.11.ffn_up.weight
create_tensor: loading tensor blk.12.attn_norm.weight
create_tensor: loading tensor blk.12.post_attention_norm.weight
create_tensor: loading tensor blk.12.attn_qkv.weight
create_tensor: loading tensor blk.12.attn_gate.weight
create_tensor: loading tensor blk.12.ssm_conv1d.weight
create_tensor: loading tensor blk.12.ssm_dt.bias
create_tensor: loading tensor blk.12.ssm_a
create_tensor: loading tensor blk.12.ssm_beta.weight
create_tensor: loading tensor blk.12.ssm_alpha.weight
create_tensor: loading tensor blk.12.ssm_norm.weight
create_tensor: loading tensor blk.12.ssm_out.weight
create_tensor: loading tensor blk.12.ffn_gate.weight
create_tensor: loading tensor blk.12.ffn_down.weight
create_tensor: loading tensor blk.12.ffn_up.weight
create_tensor: loading tensor blk.13.attn_norm.weight
create_tensor: loading tensor blk.13.post_attention_norm.weight
create_tensor: loading tensor blk.13.attn_qkv.weight
create_tensor: loading tensor blk.13.attn_gate.weight
create_tensor: loading tensor blk.13.ssm_conv1d.weight
create_tensor: loading tensor blk.13.ssm_dt.bias
create_tensor: loading tensor blk.13.ssm_a
create_tensor: loading tensor blk.13.ssm_beta.weight
create_tensor: loading tensor blk.13.ssm_alpha.weight
create_tensor: loading tensor blk.13.ssm_norm.weight
create_tensor: loading tensor blk.13.ssm_out.weight
create_tensor: loading tensor blk.13.ffn_gate.weight
create_tensor: loading tensor blk.13.ffn_down.weight
create_tensor: loading tensor blk.13.ffn_up.weight
create_tensor: loading tensor blk.14.attn_norm.weight
create_tensor: loading tensor blk.14.post_attention_norm.weight
create_tensor: loading tensor blk.14.attn_qkv.weight
create_tensor: loading tensor blk.14.attn_gate.weight
create_tensor: loading tensor blk.14.ssm_conv1d.weight
create_tensor: loading tensor blk.14.ssm_dt.bias
create_tensor: loading tensor blk.14.ssm_a
create_tensor: loading tensor blk.14.ssm_beta.weight
create_tensor: loading tensor blk.14.ssm_alpha.weight
create_tensor: loading tensor blk.14.ssm_norm.weight
create_tensor: loading tensor blk.14.ssm_out.weight
create_tensor: loading tensor blk.14.ffn_gate.weight
create_tensor: loading tensor blk.14.ffn_down.weight
create_tensor: loading tensor blk.14.ffn_up.weight
create_tensor: loading tensor blk.15.attn_norm.weight
create_tensor: loading tensor blk.15.post_attention_norm.weight
create_tensor: loading tensor blk.15.attn_q.weight
create_tensor: loading tensor blk.15.attn_k.weight
create_tensor: loading tensor blk.15.attn_v.weight
create_tensor: loading tensor blk.15.attn_output.weight
create_tensor: loading tensor blk.15.attn_q_norm.weight
create_tensor: loading tensor blk.15.attn_k_norm.weight
create_tensor: loading tensor blk.15.ffn_gate.weight
create_tensor: loading tensor blk.15.ffn_down.weight
create_tensor: loading tensor blk.15.ffn_up.weight
create_tensor: loading tensor blk.16.attn_norm.weight
create_tensor: loading tensor blk.16.post_attention_norm.weight
create_tensor: loading tensor blk.16.attn_qkv.weight
create_tensor: loading tensor blk.16.attn_gate.weight
create_tensor: loading tensor blk.16.ssm_conv1d.weight
create_tensor: loading tensor blk.16.ssm_dt.bias
create_tensor: loading tensor blk.16.ssm_a
create_tensor: loading tensor blk.16.ssm_beta.weight
create_tensor: loading tensor blk.16.ssm_alpha.weight
create_tensor: loading tensor blk.16.ssm_norm.weight
create_tensor: loading tensor blk.16.ssm_out.weight
create_tensor: loading tensor blk.16.ffn_gate.weight
create_tensor: loading tensor blk.16.ffn_down.weight
create_tensor: loading tensor blk.16.ffn_up.weight
create_tensor: loading tensor blk.17.attn_norm.weight
create_tensor: loading tensor blk.17.post_attention_norm.weight
create_tensor: loading tensor blk.17.attn_qkv.weight
create_tensor: loading tensor blk.17.attn_gate.weight
create_tensor: loading tensor blk.17.ssm_conv1d.weight
create_tensor: loading tensor blk.17.ssm_dt.bias
create_tensor: loading tensor blk.17.ssm_a
create_tensor: loading tensor blk.17.ssm_beta.weight
create_tensor: loading tensor blk.17.ssm_alpha.weight
create_tensor: loading tensor blk.17.ssm_norm.weight
create_tensor: loading tensor blk.17.ssm_out.weight
create_tensor: loading tensor blk.17.ffn_gate.weight
create_tensor: loading tensor blk.17.ffn_down.weight
create_tensor: loading tensor blk.17.ffn_up.weight
create_tensor: loading tensor blk.18.attn_norm.weight
create_tensor: loading tensor blk.18.post_attention_norm.weight
create_tensor: loading tensor blk.18.attn_qkv.weight
create_tensor: loading tensor blk.18.attn_gate.weight
create_tensor: loading tensor blk.18.ssm_conv1d.weight
create_tensor: loading tensor blk.18.ssm_dt.bias
create_tensor: loading tensor blk.18.ssm_a
create_tensor: loading tensor blk.18.ssm_beta.weight
create_tensor: loading tensor blk.18.ssm_alpha.weight
create_tensor: loading tensor blk.18.ssm_norm.weight
create_tensor: loading tensor blk.18.ssm_out.weight
create_tensor: loading tensor blk.18.ffn_gate.weight
create_tensor: loading tensor blk.18.ffn_down.weight
create_tensor: loading tensor blk.18.ffn_up.weight
create_tensor: loading tensor blk.19.attn_norm.weight
create_tensor: loading tensor blk.19.post_attention_norm.weight
create_tensor: loading tensor blk.19.attn_q.weight
create_tensor: loading tensor blk.19.attn_k.weight
create_tensor: loading tensor blk.19.attn_v.weight
create_tensor: loading tensor blk.19.attn_output.weight
create_tensor: loading tensor blk.19.attn_q_norm.weight
create_tensor: loading tensor blk.19.attn_k_norm.weight
create_tensor: loading tensor blk.19.ffn_gate.weight
create_tensor: loading tensor blk.19.ffn_down.weight
create_tensor: loading tensor blk.19.ffn_up.weight
create_tensor: loading tensor blk.20.attn_norm.weight
create_tensor: loading tensor blk.20.post_attention_norm.weight
create_tensor: loading tensor blk.20.attn_qkv.weight
create_tensor: loading tensor blk.20.attn_gate.weight
create_tensor: loading tensor blk.20.ssm_conv1d.weight
create_tensor: loading tensor blk.20.ssm_dt.bias
create_tensor: loading tensor blk.20.ssm_a
create_tensor: loading tensor blk.20.ssm_beta.weight
create_tensor: loading tensor blk.20.ssm_alpha.weight
create_tensor: loading tensor blk.20.ssm_norm.weight
create_tensor: loading tensor blk.20.ssm_out.weight
create_tensor: loading tensor blk.20.ffn_gate.weight
create_tensor: loading tensor blk.20.ffn_down.weight
create_tensor: loading tensor blk.20.ffn_up.weight
create_tensor: loading tensor blk.21.attn_norm.weight
create_tensor: loading tensor blk.21.post_attention_norm.weight
create_tensor: loading tensor blk.21.attn_qkv.weight
create_tensor: loading tensor blk.21.attn_gate.weight
create_tensor: loading tensor blk.21.ssm_conv1d.weight
create_tensor: loading tensor blk.21.ssm_dt.bias
create_tensor: loading tensor blk.21.ssm_a
create_tensor: loading tensor blk.21.ssm_beta.weight
create_tensor: loading tensor blk.21.ssm_alpha.weight
create_tensor: loading tensor blk.21.ssm_norm.weight
create_tensor: loading tensor blk.21.ssm_out.weight
create_tensor: loading tensor blk.21.ffn_gate.weight
create_tensor: loading tensor blk.21.ffn_down.weight
create_tensor: loading tensor blk.21.ffn_up.weight
create_tensor: loading tensor blk.22.attn_norm.weight
create_tensor: loading tensor blk.22.post_attention_norm.weight
create_tensor: loading tensor blk.22.attn_qkv.weight
create_tensor: loading tensor blk.22.attn_gate.weight
create_tensor: loading tensor blk.22.ssm_conv1d.weight
create_tensor: loading tensor blk.22.ssm_dt.bias
create_tensor: loading tensor blk.22.ssm_a
create_tensor: loading tensor blk.22.ssm_beta.weight
create_tensor: loading tensor blk.22.ssm_alpha.weight
create_tensor: loading tensor blk.22.ssm_norm.weight
create_tensor: loading tensor blk.22.ssm_out.weight
create_tensor: loading tensor blk.22.ffn_gate.weight
create_tensor: loading tensor blk.22.ffn_down.weight
create_tensor: loading tensor blk.22.ffn_up.weight
create_tensor: loading tensor blk.23.attn_norm.weight
create_tensor: loading tensor blk.23.post_attention_norm.weight
create_tensor: loading tensor blk.23.attn_q.weight
create_tensor: loading tensor blk.23.attn_k.weight
create_tensor: loading tensor blk.23.attn_v.weight
create_tensor: loading tensor blk.23.attn_output.weight
create_tensor: loading tensor blk.23.attn_q_norm.weight
create_tensor: loading tensor blk.23.attn_k_norm.weight
create_tensor: loading tensor blk.23.ffn_gate.weight
create_tensor: loading tensor blk.23.ffn_down.weight
create_tensor: loading tensor blk.23.ffn_up.weight
create_tensor: loading tensor blk.24.attn_norm.weight
create_tensor: loading tensor blk.24.post_attention_norm.weight
create_tensor: loading tensor blk.24.attn_qkv.weight
create_tensor: loading tensor blk.24.attn_gate.weight
create_tensor: loading tensor blk.24.ssm_conv1d.weight
create_tensor: loading tensor blk.24.ssm_dt.bias
create_tensor: loading tensor blk.24.ssm_a
create_tensor: loading tensor blk.24.ssm_beta.weight
create_tensor: loading tensor blk.24.ssm_alpha.weight
create_tensor: loading tensor blk.24.ssm_norm.weight
create_tensor: loading tensor blk.24.ssm_out.weight
create_tensor: loading tensor blk.24.ffn_gate.weight
create_tensor: loading tensor blk.24.ffn_down.weight
create_tensor: loading tensor blk.24.ffn_up.weight
create_tensor: loading tensor blk.25.attn_norm.weight
create_tensor: loading tensor blk.25.post_attention_norm.weight
create_tensor: loading tensor blk.25.attn_qkv.weight
create_tensor: loading tensor blk.25.attn_gate.weight
create_tensor: loading tensor blk.25.ssm_conv1d.weight
create_tensor: loading tensor blk.25.ssm_dt.bias
create_tensor: loading tensor blk.25.ssm_a
create_tensor: loading tensor blk.25.ssm_beta.weight
create_tensor: loading tensor blk.25.ssm_alpha.weight
create_tensor: loading tensor blk.25.ssm_norm.weight
create_tensor: loading tensor blk.25.ssm_out.weight
create_tensor: loading tensor blk.25.ffn_gate.weight
create_tensor: loading tensor blk.25.ffn_down.weight
create_tensor: loading tensor blk.25.ffn_up.weight
create_tensor: loading tensor blk.26.attn_norm.weight
create_tensor: loading tensor blk.26.post_attention_norm.weight
create_tensor: loading tensor blk.26.attn_qkv.weight
create_tensor: loading tensor blk.26.attn_gate.weight
create_tensor: loading tensor blk.26.ssm_conv1d.weight
create_tensor: loading tensor blk.26.ssm_dt.bias
create_tensor: loading tensor blk.26.ssm_a
create_tensor: loading tensor blk.26.ssm_beta.weight
create_tensor: loading tensor blk.26.ssm_alpha.weight
create_tensor: loading tensor blk.26.ssm_norm.weight
create_tensor: loading tensor blk.26.ssm_out.weight
create_tensor: loading tensor blk.26.ffn_gate.weight
create_tensor: loading tensor blk.26.ffn_down.weight
create_tensor: loading tensor blk.26.ffn_up.weight
create_tensor: loading tensor blk.27.attn_norm.weight
create_tensor: loading tensor blk.27.post_attention_norm.weight
create_tensor: loading tensor blk.27.attn_q.weight
create_tensor: loading tensor blk.27.attn_k.weight
create_tensor: loading tensor blk.27.attn_v.weight
create_tensor: loading tensor blk.27.attn_output.weight
create_tensor: loading tensor blk.27.attn_q_norm.weight
create_tensor: loading tensor blk.27.attn_k_norm.weight
create_tensor: loading tensor blk.27.ffn_gate.weight
create_tensor: loading tensor blk.27.ffn_down.weight
create_tensor: loading tensor blk.27.ffn_up.weight
create_tensor: loading tensor blk.28.attn_norm.weight
create_tensor: loading tensor blk.28.post_attention_norm.weight
create_tensor: loading tensor blk.28.attn_qkv.weight
create_tensor: loading tensor blk.28.attn_gate.weight
create_tensor: loading tensor blk.28.ssm_conv1d.weight
create_tensor: loading tensor blk.28.ssm_dt.bias
create_tensor: loading tensor blk.28.ssm_a
create_tensor: loading tensor blk.28.ssm_beta.weight
create_tensor: loading tensor blk.28.ssm_alpha.weight
create_tensor: loading tensor blk.28.ssm_norm.weight
create_tensor: loading tensor blk.28.ssm_out.weight
create_tensor: loading tensor blk.28.ffn_gate.weight
create_tensor: loading tensor blk.28.ffn_down.weight
create_tensor: loading tensor blk.28.ffn_up.weight
create_tensor: loading tensor blk.29.attn_norm.weight
create_tensor: loading tensor blk.29.post_attention_norm.weight
create_tensor: loading tensor blk.29.attn_qkv.weight
create_tensor: loading tensor blk.29.attn_gate.weight
create_tensor: loading tensor blk.29.ssm_conv1d.weight
create_tensor: loading tensor blk.29.ssm_dt.bias
create_tensor: loading tensor blk.29.ssm_a
create_tensor: loading tensor blk.29.ssm_beta.weight
create_tensor: loading tensor blk.29.ssm_alpha.weight
create_tensor: loading tensor blk.29.ssm_norm.weight
create_tensor: loading tensor blk.29.ssm_out.weight
create_tensor: loading tensor blk.29.ffn_gate.weight
create_tensor: loading tensor blk.29.ffn_down.weight
create_tensor: loading tensor blk.29.ffn_up.weight
create_tensor: loading tensor blk.30.attn_norm.weight
create_tensor: loading tensor blk.30.post_attention_norm.weight
create_tensor: loading tensor blk.30.attn_qkv.weight
create_tensor: loading tensor blk.30.attn_gate.weight
create_tensor: loading tensor blk.30.ssm_conv1d.weight
create_tensor: loading tensor blk.30.ssm_dt.bias
create_tensor: loading tensor blk.30.ssm_a
create_tensor: loading tensor blk.30.ssm_beta.weight
create_tensor: loading tensor blk.30.ssm_alpha.weight
create_tensor: loading tensor blk.30.ssm_norm.weight
create_tensor: loading tensor blk.30.ssm_out.weight
create_tensor: loading tensor blk.30.ffn_gate.weight
create_tensor: loading tensor blk.30.ffn_down.weight
create_tensor: loading tensor blk.30.ffn_up.weight
create_tensor: loading tensor blk.31.attn_norm.weight
create_tensor: loading tensor blk.31.post_attention_norm.weight
create_tensor: loading tensor blk.31.attn_q.weight
create_tensor: loading tensor blk.31.attn_k.weight
create_tensor: loading tensor blk.31.attn_v.weight
create_tensor: loading tensor blk.31.attn_output.weight
create_tensor: loading tensor blk.31.attn_q_norm.weight
create_tensor: loading tensor blk.31.attn_k_norm.weight
create_tensor: loading tensor blk.31.ffn_gate.weight
create_tensor: loading tensor blk.31.ffn_down.weight
create_tensor: loading tensor blk.31.ffn_up.weight
done_getting_tensors: tensor 'token_embd.weight' (q6_K) (and 0 others) cannot be used with preferred buffer type CUDA_Host, using CPU instead
load_tensors: offloading output layer to GPU
load_tensors: offloading 31 repeating layers to GPU
load_tensors: offloaded 33/33 layers to GPU
load_tensors: CPU model buffer size = 497.31 MiB
load_tensors: CUDA0 model buffer size = 3293.12 MiB
load_all_data: no device found for buffer type CPU for async uploads
load_all_data: using async uploads for device CUDA0, buffer type CUDA0, backend CUDA0
............................................................................
llama_context: constructing llama_context
llama_context: n_seq_max = 1
llama_context: n_ctx = 16384
llama_context: n_ctx_seq = 16384
llama_context: n_batch = 128
llama_context: n_ubatch = 128
llama_context: causal_attn = 1
llama_context: flash_attn = enabled
llama_context: kv_unified = false
llama_context: freq_base = 10000000.0
llama_context: freq_scale = 1
llama_context: n_ctx_seq (16384) < n_ctx_train (262144) -- the full capacity of the model will not be utilized
set_abort_callback: call
llama_context: CUDA_Host output buffer size = 0.95 MiB
llama_kv_cache: layer 0: filtered
llama_kv_cache: layer 1: filtered
llama_kv_cache: layer 2: filtered
llama_kv_cache: layer 3: dev = CUDA0
llama_kv_cache: layer 4: filtered
llama_kv_cache: layer 5: filtered
llama_kv_cache: layer 6: filtered
llama_kv_cache: layer 7: dev = CUDA0
llama_kv_cache: layer 8: filtered
llama_kv_cache: layer 9: filtered
llama_kv_cache: layer 10: filtered
llama_kv_cache: layer 11: dev = CUDA0
llama_kv_cache: layer 12: filtered
llama_kv_cache: layer 13: filtered
llama_kv_cache: layer 14: filtered
llama_kv_cache: layer 15: dev = CUDA0
llama_kv_cache: layer 16: filtered
llama_kv_cache: layer 17: filtered
llama_kv_cache: layer 18: filtered
llama_kv_cache: layer 19: dev = CUDA0
llama_kv_cache: layer 20: filtered
llama_kv_cache: layer 21: filtered
llama_kv_cache: layer 22: filtered
llama_kv_cache: layer 23: dev = CUDA0
llama_kv_cache: layer 24: filtered
llama_kv_cache: layer 25: filtered
llama_kv_cache: layer 26: filtered
llama_kv_cache: layer 27: dev = CUDA0
llama_kv_cache: layer 28: filtered
llama_kv_cache: layer 29: filtered
llama_kv_cache: layer 30: filtered
llama_kv_cache: layer 31: dev = CUDA0
llama_kv_cache: CUDA0 KV buffer size = 512.00 MiB
llama_kv_cache: size = 512.00 MiB ( 16384 cells, 8 layers, 1/1 seqs), K (f16): 256.00 MiB, V (f16): 256.00 MiB
llama_memory_recurrent, layer 0: dev = CUDA0
llama_memory_recurrent, layer 1: dev = CUDA0
llama_memory_recurrent, layer 2: dev = CUDA0
llama_memory_recurrent: layer 3: skipped
llama_memory_recurrent, layer 4: dev = CUDA0
llama_memory_recurrent, layer 5: dev = CUDA0
llama_memory_recurrent, layer 6: dev = CUDA0
llama_memory_recurrent: layer 7: skipped
llama_memory_recurrent, layer 8: dev = CUDA0
llama_memory_recurrent, layer 9: dev = CUDA0
llama_memory_recurrent, layer 10: dev = CUDA0
llama_memory_recurrent: layer 11: skipped
llama_memory_recurrent, layer 12: dev = CUDA0
llama_memory_recurrent, layer 13: dev = CUDA0
llama_memory_recurrent, layer 14: dev = CUDA0
llama_memory_recurrent: layer 15: skipped
llama_memory_recurrent, layer 16: dev = CUDA0
llama_memory_recurrent, layer 17: dev = CUDA0
llama_memory_recurrent, layer 18: dev = CUDA0
llama_memory_recurrent: layer 19: skipped
llama_memory_recurrent, layer 20: dev = CUDA0
llama_memory_recurrent, layer 21: dev = CUDA0
llama_memory_recurrent, layer 22: dev = CUDA0
llama_memory_recurrent: layer 23: skipped
llama_memory_recurrent, layer 24: dev = CUDA0
llama_memory_recurrent, layer 25: dev = CUDA0
llama_memory_recurrent, layer 26: dev = CUDA0
llama_memory_recurrent: layer 27: skipped
llama_memory_recurrent, layer 28: dev = CUDA0
llama_memory_recurrent, layer 29: dev = CUDA0
llama_memory_recurrent, layer 30: dev = CUDA0
llama_memory_recurrent: layer 31: skipped
llama_memory_recurrent: CUDA0 RS buffer size = 50.25 MiB
llama_memory_recurrent: size = 50.25 MiB ( 1 cells, 32 layers, 1 seqs), R (f32): 2.25 MiB, S (f32): 48.00 MiB
llama_context: enumerating backends
llama_context: backend_ptrs.size() = 2
sched_reserve: reserving ...
sched_reserve: max_nodes = 13664
sched_reserve: reserving full memory module
sched_reserve: worst-case: n_tokens = 128, n_seqs = 1, n_outputs = 1
sched_reserve: resolving fused Gated Delta Net support:
graph_reserve: reserving a graph for ubatch with n_tokens = 1, n_seqs = 1, n_outputs = 1
sched_reserve: fused Gated Delta Net (autoregressive) enabled
graph_reserve: reserving a graph for ubatch with n_tokens = 16, n_seqs = 1, n_outputs = 16
sched_reserve: fused Gated Delta Net (chunked) enabled
graph_reserve: reserving a graph for ubatch with n_tokens = 128, n_seqs = 1, n_outputs = 128
graph_reserve: reserving a graph for ubatch with n_tokens = 1, n_seqs = 1, n_outputs = 1
graph_reserve: reserving a graph for ubatch with n_tokens = 128, n_seqs = 1, n_outputs = 128
sched_reserve: CUDA0 compute buffer size = 122.50 MiB
sched_reserve: CUDA_Host compute buffer size = 10.50 MiB
sched_reserve: graph nodes = 1833
sched_reserve: graph splits = 2
sched_reserve: reserve took 11.26 ms, sched copies = 1
set_warmup: value = 1
set_warmup: value = 0
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
+25
View File
@@ -0,0 +1,25 @@
2026-03-27T15:10:47.602969Z  INFO Loading model via llama-sys model=D:\Documents\Github\ai-backend-studio\models\Qwen3.5-4B-Abliterated-Claude-4.6-Opus-Reasoning-Distilled.i1-Q6_K.gguf profile=Turbo4B n_gpu_layers=999 n_ctx=16384 n_batch=128 n_ubatch=128 flash_attn=true use_mmap=false
2026-03-27T15:10:47.603056Z  INFO Loading llama library path=llama.dll
2026-03-27T15:10:47.691116Z  INFO llama backend initialised
2026-03-27T15:10:49.551993Z  INFO Model loaded desc=qwen35 4B Q6_K params_b=4.205751296 is_hybrid=true is_recurrent=false
2026-03-27T15:10:49.664247Z  INFO Context initialised n_ctx=16384
2026-03-27T15:10:49.664284Z  INFO Running engine warmup (dummy decode)…
2026-03-27T15:10:49.722289Z  INFO Warmup complete
╔══════════════════════════════════════════════════╗
║ ForgeLM • Local LLM Runtime ║
╚══════════════════════════════════════════════════╝
Model : Qwen3.5-4B-Abliterated-Claude-4.6-Opus-Reasoning-Distilled.i1-Q6_K
API Endpoints:
GET http://127.0.0.1:18083/health
GET http://127.0.0.1:18083/v1/models
POST http://127.0.0.1:18083/v1/chat/completions
Chat UI:
file://$(pwd)/ui/chat.html
(open ui/chat.html in your browser and set server to http://127.0.0.1:18083)
2026-03-27T15:10:50.807302Z  INFO Prefill finished in 134.37 ms (2143.28 t/s)
2026-03-27T15:10:53.092803Z  INFO Generation complete tokens_generated=128 tps="56.0" prefill_ms="134.4"
+464
View File
@@ -0,0 +1,464 @@
# Autoresearch Worklog: latency-p95-chat-completions
## Session Summary
- Objective: reduce end-to-end p95 latency for local `POST /v1/chat/completions`
- Primary metric: `p95_ms` (lower is better)
- Secondary metrics: `p50_ms`, `mean_ms`, `completion_tps_mean`, `success_count`
- Benchmark harness: `./autoresearch.sh`
## Key Insights
- The first stable end-to-end benchmark is around 0.94 s p95 for a short non-streaming coding request on the local 4B GGUF model.
- The benchmark harness must use the canonical model id derived from the GGUF filename; the current server build does not actually honor the intended `local-model` alias path yet.
- Prompt prefill is only about 25 ms for this workload, so almost all latency sits in token generation rather than request parsing or prompt construction.
- A micro-optimization in per-token detokenization made both latency and throughput worse, which suggests the dominant cost is deeper in the generation/runtime path rather than heap allocation in Rust glue code.
- Shrinking the `Turbo4B` context and batch sizes also hurt throughput, so the current 4B profile appears closer to the local optimum for this hardware/workload than expected.
- Disabling internal llama.cpp perf counters produced the first real p95 improvement over baseline, which suggests a small but measurable amount of hot-path CPU overhead was coming from instrumentation rather than model math.
- Better stop behavior improved average and median latency, but it still failed to beat baseline p95, so the worst outliers are not explained solely by turn-boundary overruns.
- Switching to a Qwen-style chat template produced a severe p95 tail regression while median latency stayed near baseline, which strongly suggests worse stop behavior or occasional runaway generations rather than uniformly slower decoding.
## Next Ideas
- Measure baseline first, then inspect the hottest latency contributors in prompt preparation, request handling, and session creation.
- Investigate why `ModelCatalog` rejects `local-model` despite intending to expose aliases.
### Run 1: baseline - p95_ms=941.252 (KEEP)
- Timestamp: 2026-03-26 17:32
- What changed: Established the autoresearch harness, built the release server, started it against the local Qwen GGUF, and measured a fixed non-streaming coding request workload.
- Result: p95_ms=941.252, p50_ms=908.457, mean_ms=917.123, completion_tps_mean=52.427, success_count=7.
- Insight: The baseline is stable enough to compare future server/runtime changes, and the benchmark immediately exposed a compatibility bug in alias resolution that should be kept separate from true latency experiments.
- Next: fix low-risk benchmark-adjacent correctness issues only if they distort measurement; otherwise inspect hot latency contributors in the request/session path.
### Run 2: stack buffer detokenization - p95_ms=1032.315 (DISCARD)
- Timestamp: 2026-03-26 17:38
- What changed: Replaced the per-token heap allocation in `decode_token_piece_with()` with a 256-byte stack buffer and heap fallback in `crates/inference-core/src/session.rs`.
- Result: p95_ms=1032.315, p50_ms=1020.402, mean_ms=1011.466, completion_tps_mean=47.671, success_count=7.
- Insight: This path is not the practical bottleneck for end-to-end latency here; generation got slower overall despite the reduced allocation churn.
- Next: discard this branch state and focus on runtime/profile-level experiments that can improve generation throughput directly.
### Run 3: slim turbo4b runtime profile - p95_ms=1004.435 (DISCARD)
- Timestamp: 2026-03-26 17:40
- What changed: Reduced `Turbo4B` defaults in `crates/inference-core/src/profile.rs` from `n_ctx=4096`, `n_batch=512`, `n_ubatch=128` to `n_ctx=2048`, `n_batch=256`, `n_ubatch=64`.
- Result: p95_ms=1004.435, p50_ms=998.919, mean_ms=990.771, completion_tps_mean=48.528, success_count=7.
- Insight: The leaner profile still reduced effective generation speed on this RTX 3070 + Q6_K workload, so the current batch/context sizing is likely not the source of the baseline latency.
- Next: revert to the best-known commit and target generation semantics or model-facing prompt/template strategy rather than detokenization or profile shrinkage.
### Run 6: disable llama perf counters in hot path - p95_ms=925.587 (KEEP)
- Timestamp: 2026-03-26 17:57
- What changed: Set `llama_context_params.no_perf = true` in `crates/inference-core/src/engine.rs` and `sampler_chain_default_params.no_perf = true` in `crates/inference-core/src/session.rs`.
- Result: p95_ms=925.587, p50_ms=893.913, mean_ms=899.895, completion_tps_mean=53.618, success_count=7.
- Insight: Disabling internal perf bookkeeping shaved about 1.7% off baseline p95 with slightly better overall throughput, making this the first experiment worth keeping.
- Next: continue from this commit and look for adjacent hot-path flags or runtime settings that reduce generation overhead without changing model behavior.
### Run 5: default turn-boundary stops - p95_ms=965.663 (DISCARD)
- Timestamp: 2026-03-26 17:54
- What changed: Added rolling multi-token stop detection in `crates/inference-core/src/session.rs` and injected default turn-boundary stop sequences in `crates/inference-server/src/compat.rs` when the request did not specify `stop`.
- Result: p95_ms=965.663, p50_ms=877.030, mean_ms=897.251, completion_tps_mean=53.686, success_count=7.
- Insight: This reduced median and mean latency and slightly improved throughput, but the p95 still stayed above baseline, so tail latency likely comes from a smaller number of deeper generation stalls rather than just missed stop boundaries.
- Next: inspect per-sample latency spread and target the remaining long-tail outliers directly, likely in warmup/first-request behavior or occasional decode stalls.
### Run 4: qwen-style chat template - p95_ms=3027.145 (DISCARD)
- Timestamp: 2026-03-26 17:50
- What changed: Replaced the plain `System/User/Assistant:` prompt format in `crates/inference-server/src/compat.rs` with a Qwen-style `<|im_start|>...<|im_end|>` chat template.
- Result: p95_ms=3027.145, p50_ms=974.937, mean_ms=1467.026, completion_tps_mean=41.360, success_count=7.
- Insight: The template did not make typical requests much faster and introduced severe tail latency, likely because some generations stopped much later or followed a worse decoding path for this server/model combination.
- Next: revert this template change and focus on experiments that directly bound generation length or improve stop behavior without overhauling the prompt format.
## Segment 1: Dynamic Chat Templates
- New objective: add dynamic prompt templates by model family with built-in coverage for Qwen, Gemma, Llama, Kimi, and a generic fallback.
- Benchmark driver: `./autoresearch.sh` now runs ignored template-specific tests and reports `template_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule for this segment is test-driven: higher `template_score` wins, while the regular non-ignored test suite must remain green.
- A minimal family detector plus embedded template registry in `compat.rs` was enough to move coverage from 1/5 to 5/5 without touching the runtime.
### Run 7: baseline - template_score=20.0 (KEEP)
- Timestamp: 2026-03-26 18:14
- What changed: Re-initialized autoresearch to a new segment focused on dynamic chat templates, added a dedicated ignored test suite for Qwen, Gemma, Llama, Kimi, and generic fallback behavior, and switched the benchmark harness to score that suite.
- Result: template_score=20.0, passed_cases=1, failed_cases=4, total_cases=5.
- Insight: Only the generic fallback works today; all named family-template expectations fail because prompt rendering is still generic and model-agnostic.
- Next: add a minimal model-family detector and template registry in `crates/inference-server/src/compat.rs`, then route `prepare_prompt()` through it.
### Run 8: add family-based chat templates - template_score=100.0 (KEEP)
- Timestamp: 2026-03-26 18:16
- What changed: Added model-family detection and template-aware prompt rendering in `crates/inference-server/src/compat.rs` for Qwen, Gemma, Llama, Kimi, and generic fallback, then routed system/tool/JSON-mode instructions and assistant prefixes through the selected template.
- Result: template_score=100.0, passed_cases=5, failed_cases=0, total_cases=5.
- Insight: The current seam in `prepare_prompt()` is sufficient for broad family-based template support; no runtime or routing changes were needed for the first successful implementation.
- Next: harden the design by adding alias-driven family detection, safer fallback behavior for mixed-case/variant model names, and more coverage for tool-call and response-format rendering under non-generic templates.
## Segment 2: Dynamic Chat Template Hardening
- New objective: keep family-based template selection, but harden structure-sensitive formatting for instruct-style models, starting with Llama.
- Benchmark driver: `./autoresearch.sh` still runs the ignored template suite, now with stricter Llama assertions.
- Keep/discard rule remains test-driven: higher `template_score` wins while the regular non-ignored suite stays green.
### Run 9: baseline - template_score=71.429 (KEEP)
- Timestamp: 2026-03-26 18:21
- What changed: Expanded the ignored benchmark suite with two stronger Llama assertions that require system+user to be merged into the first instruction block and forbid leaving an open `[INST]` prefix at generation time.
- Result: template_score=71.429, passed_cases=5, failed_cases=2, total_cases=7.
- Insight: The current family detector is good enough, but the Llama renderer is still structurally naive; it over-produces instruction blocks and leaves the prompt in an invalid generation state.
- Next: refactor Llama rendering into a dedicated formatter that folds system context into the first user turn and emits a proper assistant generation prefix.
### Run 10: harden llama chat template rendering - template_score=100.0 (KEEP)
- Timestamp: 2026-03-26 18:23
- What changed: Reworked the Llama path in `crates/inference-server/src/compat.rs` into a dedicated renderer that accumulates pending system/developer content, folds it into the next user/tool instruction block, and stops emitting a dangling open assistant `[INST]` prefix.
- Result: template_score=100.0, passed_cases=7, failed_cases=0, total_cases=7.
- Insight: Llama needed a specialized formatter instead of the generic per-message helper; once the prompt was rendered as proper closed instruction blocks with folded system context, the hardening suite passed cleanly.
- Next: raise the benchmark again around multi-turn continuity, tool-role placement, or alias-driven family detection so the next experiments target real edge cases instead of already-solved happy paths.
## Segment 3: Alias-Driven Template Selection
- New objective: harden family detection so template selection can still infer the right family from an alias even when the canonical/root model id is generic.
- Benchmark driver: `./autoresearch.sh` still runs the ignored template suite, now expanded with alias-sensitive cases.
- Keep/discard rule remains test-driven: higher `template_score` wins while the regular non-ignored suite stays green.
### Run 11: baseline - template_score=88.889 (KEEP)
- Timestamp: 2026-03-26 18:27
- What changed: Expanded the ignored benchmark suite with alias-sensitive family-selection coverage, especially a case where the canonical model id is generic but the requested alias carries the `qwen` family hint.
- Result: template_score=88.889, passed_cases=8, failed_cases=1, total_cases=9.
- Insight: The family templates and fallback behavior are otherwise stable; the remaining gap is specifically that `select_chat_template()` loses the alias hint when it resolves through a generic canonical/root id.
- Next: make template selection consider both the requested model string and the resolved catalog/root metadata, preferring whichever one contains a recognized family marker.
### Run 12: detect template family from aliases - template_score=100.0 (KEEP)
- Timestamp: 2026-03-26 18:28
- What changed: Updated `select_chat_template()` in `crates/inference-server/src/compat.rs` to evaluate the requested model name first, then resolved alias/canonical/root ids, so family hints present only in aliases still drive template selection.
- Result: template_score=100.0, passed_cases=9, failed_cases=0, total_cases=9.
- Insight: Alias-aware selection closes the last known family-detection gap without needing extra catalog metadata; simple ordered candidate matching is enough here.
- Next: raise the benchmark again around multi-turn continuity, tool-call/tool-role rendering, or family-specific response-format instructions to target subtler correctness gaps.
## Segment 4: Variant Family Detection
- New objective: harden family detection for common model-name variants, starting with Moonshot-branded Kimi aliases.
- Benchmark driver: `./autoresearch.sh` still runs the ignored template suite, now expanded with variant-sensitive cases.
- Keep/discard rule remains test-driven: higher `template_score` wins while the regular non-ignored suite stays green.
### Run 13: baseline - template_score=90.909 (KEEP)
- Timestamp: 2026-03-26 18:32
- What changed: Expanded the ignored template suite with Moonshot/Kimi variant coverage, including one positive Moonshot alias case and one near-name generic fallback case.
- Result: template_score=90.909, passed_cases=10, failed_cases=1, total_cases=11.
- Insight: The only remaining gap is that `select_chat_template()` recognizes `kimi` but not the common `moonshot` naming variant; nearby names like `moonshine` already fall back safely.
- Next: extend family detection so Moonshot-branded aliases map to the Kimi template without broadening the match so far that unrelated names stop using the generic fallback.
### Run 14: detect moonshot as kimi family - template_score=100.0 (KEEP)
- Timestamp: 2026-03-26 18:33
- What changed: Extended `select_chat_template()` in `crates/inference-server/src/compat.rs` so candidate names containing `moonshot` are treated as the Kimi family alongside direct `kimi` matches.
- Result: template_score=100.0, passed_cases=11, failed_cases=0, total_cases=11.
- Insight: A narrow Moonshot variant match closes the Kimi-family naming gap cleanly without breaking safe generic fallback for unrelated names such as `moonshine-local`.
- Next: raise the benchmark around multi-turn continuity, tool-role rendering, or family-specific response-format instructions.
## Segment 5: CLI Control Surface
- New objective: build a complete management CLI first, ahead of the Tauri app, on top of the current backend version.
- Benchmark driver: `./autoresearch.sh` now runs ignored CLI-surface tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule is test-driven: higher `cli_surface_score` wins while the regular non-ignored suite remains green.
- This segment starts by benchmarking the command surface itself before deeper execution and backend-admin semantics are implemented.
### Run 15: baseline - cli_surface_score=18.182 (KEEP)
- Timestamp: 2026-03-26 18:53
- What changed: Initialized a new `crates/localllm-cli` crate with a minimal Clap-based CLI scaffold, switched the autoresearch harness from template tests to CLI surface tests, and defined the first ignored benchmark suite for professional command coverage.
- Result: cli_surface_score=18.182, passed_cases=2, failed_cases=9, total_cases=11.
- Insight: The scaffold already covers global flags plus `status` and `models list`, but almost the entire promised management surface is still missing.
- Next: add the remaining top-level command groups and model/auth/config/bench/logs/doctor subcommands so the CLI surface matches the intended product contract before wiring execution internals.
### Run 16: expand cli command surface - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 18:55
- What changed: Expanded `crates/localllm-cli/src/main.rs` into a broad Clap parser with top-level `status`, `models`, `auth`, `config`, `bench`, `logs`, and `doctor` commands plus the expected model/auth/config/throughput subcommands and global flags.
- Result: cli_surface_score=100.0, passed_cases=11, failed_cases=0, total_cases=11.
- Insight: The professional CLI contract can be locked in quickly at the parser layer before execution internals exist; Clap gives a maintainable foundation for the full management surface.
- Next: raise the benchmark from command presence to semantic correctness, starting with command-specific arguments, defaults, and output-contract expectations for the highest-value workflows.
## Segment 6: CLI Semantic Contracts
- New objective: keep the CLI surface stable, but raise the benchmark so key commands preserve useful semantic detail such as defaults, high-value arguments, and summary/intention contracts.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
## Segment 7: CLI Execution Plans
- New objective: raise the CLI benchmark from human-readable semantic summaries to machine-usable execution plans for key workflows.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 19: baseline - cli_surface_score=69.565 (KEEP)
- Timestamp: 2026-03-26 19:04
- What changed: Expanded the CLI benchmark with plan-oriented ignored tests that require key commands to map into machine-usable `CommandPlan` variants instead of only semantic summaries.
- Result: cli_surface_score=69.565, passed_cases=16, failed_cases=7, total_cases=23.
- Insight: The semantic summary layer is already solid, but `plan_command()` is still a stub that collapses everything to `SummaryOnly`, so execution planning has not been wired yet.
- Next: implement concrete `CommandPlan` mapping for status, models scan/load, auth enable, config set, bench throughput, and logs.
### Run 20: add cli execution plans - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:05
- What changed: Implemented concrete `CommandPlan` mapping in `crates/localllm-cli/src/main.rs` for status, model scan/load, auth enable, config set, throughput bench, and logs while leaving lower-priority commands on `SummaryOnly`.
- Result: cli_surface_score=100.0, passed_cases=23, failed_cases=0, total_cases=23.
- Insight: The CLI now has a machine-usable planning layer for the highest-value workflows, which is a stronger base for later real handlers and Tauri reuse than summary strings alone.
- Next: raise the benchmark from structured plans to execution-ready request payloads or output contracts at the backend/control-plane seam.
## Segment 8: CLI Execution Contracts
- New objective: turn the highest-value `CommandPlan` variants into concrete execution contracts that describe the exact HTTP or local operation each workflow intends to perform.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 21: baseline - cli_surface_score=76.667 (KEEP)
- Timestamp: 2026-03-26 19:11
- What changed: Expanded the benchmark from `CommandPlan` semantics to `ExecutionContract` semantics, requiring the CLI to describe the concrete HTTP or local operation each high-value workflow intends to perform.
- Result: cli_surface_score=76.667, passed_cases=23, failed_cases=7, total_cases=30.
- Insight: The planning layer is now solid, but `execution_contract()` is still stubbed and returns `SummaryOnly { command: "unplanned" }` for every concrete plan, so the CLI cannot yet describe its backend/control-plane seam accurately.
- Next: map the high-value `CommandPlan` variants into concrete HTTP/local execution contracts for status, models scan/load, auth enable, config set, throughput bench, and logs.
### Run 22: add cli execution contracts - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:14
- What changed: Implemented concrete `execution_contract()` mapping in `crates/localllm-cli/src/main.rs`, turning the highest-value `CommandPlan` variants into explicit HTTP or local contracts for health probing, model scan/load, auth/config mutation, throughput requests, and log reads.
- Result: cli_surface_score=100.0, passed_cases=30, failed_cases=0, total_cases=30.
- Insight: The CLI now has a clean progression from parser -> semantic summary -> machine-usable plan -> concrete execution contract for the highest-value workflows, which is a strong base for wiring real handlers next.
- Next: raise the benchmark from contract shape to actual execution stubs or backend request payload semantics, especially for status, throughput bench, and model operations.
### Run 17: baseline - cli_surface_score=68.75 (KEEP)
- Timestamp: 2026-03-26 18:59
- What changed: Raised the benchmark with summary-focused ignored tests that require `render_command_summary()` to preserve output format, models directory, command-specific arguments, and key defaults for status, models load, auth enable, bench throughput, and logs.
- Result: cli_surface_score=68.75, passed_cases=11, failed_cases=5, total_cases=16.
- Insight: The parser surface is solid, but the current summary layer is too shallow; it only reports the top-level command and server, losing the semantic detail needed for later execution planning.
- Next: upgrade `render_command_summary()` so it emits nested command paths and the highest-value arguments/defaults for each management workflow.
### Run 18: enrich cli semantic summaries - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:00
- What changed: Reworked `render_command_summary()` in `crates/localllm-cli/src/main.rs` to emit nested command paths plus key defaults and high-value arguments for status, models, auth, config, bench, and logs workflows.
- Result: cli_surface_score=100.0, passed_cases=16, failed_cases=0, total_cases=16.
- Insight: A lightweight semantic/intention layer is enough to make the CLI contract meaningfully richer before wiring real handlers; the CLI now preserves actionable detail instead of collapsing everything to top-level groups.
- Next: raise the benchmark again toward execution planning or output contracts, e.g. structured action plans for backend/admin operations rather than just human-readable summaries.
## Segment 9: CLI Execution Stubs
- New objective: raise the CLI benchmark from abstract execution contracts to concrete execution stubs that preserve HTTP method/path/body semantics or named local actions for the highest-value workflows.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 23: baseline - cli_surface_score=83.333 (KEEP)
- Timestamp: 2026-03-26 19:20
- What changed: Expanded the benchmark from execution contracts to concrete execution stubs, adding ignored tests for HTTP method/path/body semantics and named local actions for status, model scan/load, auth enable, throughput bench, and logs.
- Result: cli_surface_score=83.333, passed_cases=30, failed_cases=6, total_cases=36.
- Insight: The CLI already knows how to parse commands and describe execution contracts, but `execution_stub()` is still just a placeholder returning `SummaryOnly { command: "unimplemented" }` for every high-value workflow.
- Next: implement concrete stub mapping so status and throughput produce execution-ready HTTP request shapes and local workflows emit named local actions with preserved detail.
### Run 24: add cli execution stubs - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:22
- What changed: Implemented concrete `execution_stub()` mapping in `crates/localllm-cli/src/main.rs`, turning the highest-value execution contracts into execution-ready HTTP requests and named local actions with preserved detail.
- Result: cli_surface_score=100.0, passed_cases=36, failed_cases=0, total_cases=36.
- Insight: The CLI now has a full design ladder for the key workflows - parser, semantic summary, machine-usable plan, execution contract, and finally execution stub - which is a strong handoff point for wiring real handlers next.
- Next: raise the benchmark from stub shape to actual handler behavior or request payload construction for the same high-value workflows.
## Segment 10: CLI Execution Previews
- New objective: raise the CLI benchmark from execution stubs to dry-run previews that expand the same high-value workflows into full previewable HTTP URLs or local-effect descriptions.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 25: baseline - cli_surface_score=85.714 (KEEP)
- Timestamp: 2026-03-26 19:27
- What changed: Expanded the benchmark from execution stubs to dry-run previews, adding ignored tests for full preview URLs and human-readable local-effect descriptions across status, models scan/load, auth enable, throughput bench, and logs.
- Result: cli_surface_score=85.714, passed_cases=36, failed_cases=6, total_cases=42.
- Insight: The CLI already models execution stubs correctly, but `execution_preview()` still collapses every high-value workflow to `SummaryOnly { command: "preview-unimplemented" }`, so there is no user-facing dry-run layer yet.
- Next: map the highest-value execution stubs into concrete `HttpPreview` and `LocalPreview` values using the resolved `--server` base URL and readable local-effect text.
### Run 26: add cli execution previews - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:28
- What changed: Implemented concrete `execution_preview()` mapping in `crates/localllm-cli/src/main.rs`, expanding HTTP stubs into full URLs with preserved payloads and local actions into readable effect descriptions via `preview_effect()`.
- Result: cli_surface_score=100.0, passed_cases=42, failed_cases=0, total_cases=42.
- Insight: The CLI now has a complete dry-run preview layer for the highest-value workflows, which is a strong bridge from internal planning seams to future real handlers and `--dry-run` UX.
- Next: raise the benchmark from preview shape to actual handler behavior or request-body construction details for status, throughput, and model-management workflows.
## Segment 11: CLI Dry-Run Output
- New objective: raise the CLI benchmark from internal preview objects to final user-facing `--dry-run` output in both `text` and `json`, so the same high-value workflows are ready for a real preview UX at the command line.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 27: baseline - cli_surface_score=87.5 (KEEP)
- Timestamp: 2026-03-26 19:32
- What changed: Expanded the benchmark from internal `ExecutionPreview` objects to final `--dry-run` UX in both text and JSON, while keeping `render_execution_preview()` intentionally shallow as `DRY RUN {summary}`.
- Result: cli_surface_score=87.5, passed_cases=42, failed_cases=6, total_cases=48.
- Insight: The CLI already knows how to build the preview object graph, but the final presentation layer still collapses to summaries instead of rendering health/chat HTTP previews and local-effect dry-run text.
- Next: render the existing preview layer into final text/json output using the current `ExecutionPreview` data rather than introducing a new execution seam.
### Run 28: render cli dry-run previews - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:34
- What changed: Implemented final `--dry-run` rendering in `crates/localllm-cli/src/main.rs`, mapping `ExecutionPreview` values to user-facing text and JSON output and reusing the existing preview pipeline (`plan_command` -> `execution_contract` -> `execution_stub` -> `execution_preview`).
- Result: cli_surface_score=100.0, passed_cases=48, failed_cases=0, total_cases=48.
- Insight: The CLI now has a complete dry-run stack for the highest-value workflows, from parser through final user-visible text/json previews, without yet executing real side effects.
- Next: raise the bar from dry-run rendering to real handler behavior or deterministic request/body construction for backend-facing commands.
## Segment 12: CLI Backend Invocations
- New objective: raise the CLI benchmark from dry-run rendering to the first real backend-call specifications for commands that can already work today, especially `status`, `models list`, and `bench throughput`.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
## Segment 13: CLI Backend Requests
- New objective: raise the CLI benchmark from backend invocation specs to executable backend request construction for the commands that already align with live server endpoints today.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 29: baseline - cli_surface_score=94.118 (KEEP)
- Timestamp: 2026-03-26 19:39
- What changed: Expanded the benchmark from final dry-run rendering to backend invocation specs for commands that can already work today, adding ignored tests for live `GET /health`, live `GET /v1/models`, and a throughput `POST /v1/chat/completions` request that preserves the selected model and token budget.
- Result: cli_surface_score=94.118, passed_cases=48, failed_cases=3, total_cases=51.
- Insight: The CLI already handled every previous parser/planning/dry-run layer, but `backend_invocation()` was still a placeholder, and `models list` had not yet been promoted into the real backend-facing seam.
- Next: implement concrete backend invocation specs for `status`, `models list`, and `bench throughput` using the resolved server base URL and auth presence.
### Run 30: add cli backend invocation specs - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:41
- What changed: Implemented `backend_invocation(&Cli)` in `crates/localllm-cli/src/main.rs` so `status` maps to live `GET /health`, `models list` maps to live `GET /v1/models`, and `bench throughput` maps to a live `POST /v1/chat/completions` invocation that preserves `--model`, samples, token budget, and auth presence.
- Result: cli_surface_score=100.0, passed_cases=51, failed_cases=0, total_cases=51.
- Insight: The CLI now crosses from dry-run-only UX into concrete backend-facing invocation specs for commands that can already work against the current server today, without needing the future control-plane endpoints yet.
- Next: raise the bar from backend invocation specs to actual request payload construction or the first executable handlers for these already-supported commands.
### Run 31: baseline - cli_surface_score=94.444 (KEEP)
- Timestamp: 2026-03-26 19:47
- What changed: Expanded the benchmark from backend invocation specs to executable backend request construction, requiring final HTTP method/url/header/body request objects with deterministic JSON payloads for `status`, `models list`, and `bench throughput`.
- Result: cli_surface_score=94.444, passed_cases=51, failed_cases=3, total_cases=54.
- Insight: The CLI already had parser, summaries, plans, contracts, stubs, previews, dry-run rendering, and invocation specs, but `backend_request(&Cli)` still returned `SummaryOnly { command: "request-unimplemented" }` instead of final request objects.
- Next: map `BackendInvocation::HttpCall` into executable `BackendRequest::HttpRequest` values with bearer headers, JSON content-type, and deterministic benchmark payload generation.
### Run 32: add cli backend request specs - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 19:49
- What changed: Implemented `backend_request(&Cli)` in `crates/localllm-cli/src/main.rs` plus helpers for bearer headers, JSON headers, deterministic benchmark prompt construction, and exact chat payload generation for throughput requests.
- Result: cli_surface_score=100.0, passed_cases=54, failed_cases=0, total_cases=54.
- Insight: The first live-compatible CLI workflows now preserve the full executable backend request boundary - method, URL, auth headers, content-type, and deterministic JSON body - while still remaining execution-free.
- Next: raise the bar from request construction to the first live response-handling or executable handler seams for the same workflows.
## Segment 14: CLI Backend Responses
- New objective: raise the CLI benchmark from executable backend request construction to backend response interpretation for the same live-compatible workflows, starting with `status`, `models list`, and `bench throughput`.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 33: baseline - cli_surface_score=94.737 (KEEP)
- Timestamp: 2026-03-26 20:01
- What changed: Added the segment 14 response-preview seam and local deserializable DTOs for health, models list, and throughput usage responses, but left `backend_response_preview()` intentionally unimplemented for the new live-compatible workflows.
- Result: cli_surface_score=94.737, passed_cases=54, failed_cases=3, total_cases=57.
- Insight: The CLI already had every layer through executable backend request construction, but it still collapsed real response parsing for `status`, `models list`, and `bench throughput` to `SummaryOnly { command: "response-unimplemented" }`.
- Next: parse the real response bodies into CLI-local previews for health status, model ids/count, and throughput usage metrics.
### Run 34: add cli backend response previews - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 20:02
- What changed: Implemented `backend_response_preview()` in `crates/localllm-cli/src/main.rs` so `status` parses health payloads, `models list` collects model ids/count, and `bench throughput` reads `usage.total_tokens` plus `usage.tokens_per_second` into CLI-local preview values.
- Result: cli_surface_score=100.0, passed_cases=57, failed_cases=0, total_cases=57.
- Insight: The first live-compatible CLI workflows now preserve the full response side of the contract too: request construction plus deterministic response interpretation without forcing reuse of server-only DTOs.
- Next: raise the bar from response previews to the first real executable handlers or user-facing live output rendering for commands that already work against the current backend.
## Segment 15: CLI Live Output
- New objective: raise the CLI benchmark from internal backend response previews to final user-facing live output rendering for the already live-compatible workflows `status`, `models list`, and `bench throughput`.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 35: baseline - cli_surface_score=90.476 (KEEP)
- Timestamp: 2026-03-26 20:06
- What changed: Expanded the benchmark from backend response previews to final user-facing live output rendering, but left `render_backend_response()` as a shallow `LIVE {summary}` placeholder.
- Result: cli_surface_score=90.476, passed_cases=57, failed_cases=6, total_cases=63.
- Insight: The CLI already parsed health, model-list, and throughput responses correctly, but it still lacked final text/json presentation for those live-compatible workflows.
- Next: render `BackendResponsePreview` into final `STATUS`, `MODELS`, and `THROUGHPUT` text/json outputs.
### Run 36: render cli live outputs - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 20:07
- What changed: Implemented `render_backend_response()` in `crates/localllm-cli/src/main.rs` so the live-compatible commands now render final text/json output directly from `BackendResponsePreview`, including health summaries, model-id lists, and throughput usage output.
- Result: cli_surface_score=100.0, passed_cases=63, failed_cases=0, total_cases=63.
- Insight: The CLI now closes the full loop for the first live-compatible workflows, from parser and planning all the way to final user-facing live output.
- Next: move from live output rendering to the first real executable handlers for commands that already work today.
## Segment 16: CLI Live Execution
- New objective: raise the CLI benchmark from final live output rendering to the first real executable handlers for the live-compatible workflows `status`, `models list`, and `bench throughput` using an injectable transport seam.
- Benchmark driver: `./autoresearch.sh` still runs ignored `localllm-cli` tests and reports `cli_surface_score`, `passed_cases`, `failed_cases`, and `total_cases`.
- Keep/discard rule remains test-driven: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 37: baseline - cli_surface_score=95.455 (KEEP)
- Timestamp: 2026-03-26 20:12
- What changed: Expanded the benchmark from live output rendering to an injectable live-execution seam, but left `execute_live_command_with()` as a placeholder that ignored the transport response and returned `LIVE EXEC {summary}`.
- Result: cli_surface_score=95.455, passed_cases=63, failed_cases=3, total_cases=66.
- Insight: The pipeline already had correct request construction and response rendering, but the executor still dropped the returned status/body instead of feeding them into the live renderer.
- Next: wire the transport response through `render_backend_response()` without changing any other CLI seam.
### Run 38: add cli live execution pipeline - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 20:13
- What changed: Implemented `execute_live_command_with()` in `crates/localllm-cli/src/main.rs` so live-compatible commands now call the injected transport with `BackendRequest`, then pass the returned `status_code` and `body` into `render_backend_response()`.
- Result: cli_surface_score=100.0, passed_cases=66, failed_cases=0, total_cases=66.
- Insight: The CLI now completes the first fully executable end-to-end path for the live-compatible workflows, from parser to transport invocation to final rendered output.
- Next: move from transport-injected execution to the first concrete HTTP client seam for commands that can already hit the current backend directly.
## Segment 17: CLI ureq HTTP Transport
- Objective: replace the injectable fake transport with a concrete `ureq` HTTP client for `status`, `models list`, and `bench throughput`.
- Benchmark: `./autoresearch.sh` runs ignored `cli_surface_*` tests in `crates/localllm-cli/src/main.rs`.
- Keep/discard rule: higher `cli_surface_score` wins while the normal non-ignored suites remain green.
### Run 39: add ureq http transport and execute_live_command - cli_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 20:36
- What changed: Added `ureq` to workspace and `localllm-cli` deps. Implemented `execute_live_command(cli)` using `ureq` as the real HTTP transport, delegating to `execute_live_command_with()`. Updated `main()` to call `execute_live_command()` instead of the scaffold placeholder. Added 3 new ignored benchmark tests verifying the injectable seam correctly routes status/models/bench through the transport pipeline.
- Result: cli_surface_score=100.0, passed_cases=69, failed_cases=0, total_cases=69.
- Insight: The CLI now has a real HTTP client (`ureq`) wired end-to-end for live-compatible commands. The injectable transport seam remains available for testing without a running server. The CLI is functionally complete for the commands that the current backend already supports.
- Next: CLI is at natural saturation given the current backend. Further progress requires new admin/control-plane endpoints in the backend for scan, load, auth, config, and watch commands.
## Segment 18: Unified Tauri Runtime Manager
- Objective: replace the split desktop runtime control flow with one saved runtime-manager surface that owns local startup, remote routing, and local model/runtime settings.
- Benchmark: targeted verification bundle using `npm test`, `npm run build`, and `cargo check` plus acceptance checks for a single runtime control plane and updated runtime UI.
- Keep/discard rule: a keep requires one working config/start path, no broken sidebar start button, and green verification across frontend and Rust checks.
### Run 40: unify runtime manager config/start flow and refactor runtime UI - runtime_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 23:35
- What changed: Removed the active dependency on `useServerProcess` and the embedded legacy `ServerPanel` from the sidebar flow, rebuilt `RuntimeSettingsDrawer` into a full local/remote runtime workspace, added Tauri wrappers for GPU/model picking and scanning, made the sidebar runtime card route setup/start from the same saved runtime state, and refreshed runtime/sidebar/global styling toward a cleaner AI desktop identity.
- Result: runtime_surface_score=100.0, unit_passed=1, build_passed=1, cargo_check_passed=1, unified_control_plane=1, design_refactor_done=1.
- Insight: The runtime failures were caused by state ownership drift, not startup logic alone. Once local runtime configuration and startup lived in the same domain, both runtime entry points aligned and the broken start path disappeared.
- Next: improve readiness probing beyond raw TCP-open detection, then smoke-test the desktop runtime end to end against a real local model and record a stricter follow-up score for startup/readiness behavior.
### Run 41: replace tcp-only readiness with /health readiness probe - runtime_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 23:43
- What changed: Replaced the runtime startup readiness check in `tauri-app/src-tauri/src/runtime/health.rs` so it now issues `GET /health`, requires an HTTP 200 response, and validates `{"status":"ok"}` before marking the local API as ready. Added Rust tests covering healthy HTTP success, unhealthy JSON payload rejection, and plain-open-port rejection.
- Result: runtime_surface_score=100.0, unit_passed=1, build_passed=1, cargo_check_passed=1, health_probe_passed=1.
- Insight: TCP-open was too weak for a desktop-managed API because any bound socket could masquerade as readiness. A lightweight health request gives the runtime manager a much more truthful startup signal without introducing frontend complexity.
- Next: run an end-to-end desktop smoke test against a real local model so the next segment can score startup behavior from the actual app surface, not just backend verification.
### Run 42: add runtime UI smoke coverage and preserve saved model on empty rescans - runtime_surface_score=100.0 (KEEP)
- Timestamp: 2026-03-26 23:50
- What changed: Added jsdom-based runtime smoke tests for `RuntimeStatusCard` and `RuntimeSettingsDrawer`, installed `jsdom` for Vitest browserless component coverage, and fixed `chooseModel()` in `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` so a previously saved `model_path` is preserved when a models-folder rescan returns no results.
- Result: runtime_surface_score=100.0, unit_passed=1, build_passed=1, cargo_check_passed=1, runtime_smoke_passed=1.
- Insight: The first useful app-surface smoke test immediately caught a real regression that backend/runtime checks could not see: a valid saved model could disappear from drawer state after an empty scan, which silently disabled the unified start action.
- Next: push the smoke coverage one step further with a focused end-to-end startup path check, ideally validating that a saved local config transitions from setup -> starting -> ready correctly from the desktop surface.
## Segment 19: Tauri Runtime Lifecycle Acceptance
- Objective: replace the stale CLI-oriented benchmark with a Tauri desktop lifecycle benchmark and harden the app-surface contract around local/remote mode transitions.
- Benchmark: `./autoresearch.sh` now runs `experiments/bench_tauri_runtime_surface.py`, which scores runtime-focused frontend tests plus `npm run build` and `cargo check`.
- Keep/discard rule: higher `runtime_lifecycle_score` wins while the active desktop runtime CTA and verification bundle remain green.
### Run 43: align the Tauri lifecycle harness and keep the local CTA visible during mode switches - runtime_lifecycle_score=100.0 (KEEP)
- Timestamp: 2026-03-27 01:26
- What changed: Replaced the stale CLI benchmark in `autoresearch.sh` with a new `experiments/bench_tauri_runtime_surface.py` driver, added a runtime smoke test that switches `RuntimeSettingsDrawer` from remote to local mode, and fixed the drawer footer in `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` so the local primary CTA is keyed off the edited local `mode` instead of the previously saved `snapshot.config.connection_mode`.
- Result: runtime_lifecycle_score=100.0, unit_passed=1, build_passed=1, cargo_check_passed=1, local_mode_cta_passed=1.
- Insight: The previous segment was saturated partly because the harness was no longer measuring the live desktop contract. Once the benchmark targeted the Tauri runtime surface directly, it immediately exposed a real lifecycle regression: switching from remote to local mode could hide the apply/start CTA before save, making the saved local flow feel broken even though the backend state was valid.
- Next: raise the lifecycle score again with a stricter saved-startup path check, ideally covering the `stopped -> starting -> ready` transition and degraded behavior after a failed health revalidation.
### Run 44: cover saved local startup transitions and local onboarding from browse to ready - runtime_lifecycle_score=100.0 (KEEP)
- Timestamp: 2026-03-27 01:40
- What changed: Extended `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` with two hook-backed lifecycle checks that run through `useRuntimeManager`: one now requires a saved local config to expose `Local API offline -> Starting local API -> Ready`, and another exercises local onboarding by browsing a models folder, auto-selecting the discovered model, saving config, and starting the local API from the same drawer flow. Updated `tauri-app/src/features/runtime/useRuntimeManager.ts` to publish optimistic `starting` and `stopping` snapshots while the Tauri commands are in flight. Raised `experiments/bench_tauri_runtime_surface.py` so the benchmark now records `local_mode_cta_passed`, `saved_startup_flow_passed`, and `local_onboarding_flow_passed` as explicit lifecycle checks alongside the broader runtime suite.
- Result: runtime_lifecycle_score=100.0, unit_passed=1, build_passed=1, cargo_check_passed=1, local_mode_cta_passed=1, saved_startup_flow_passed=1, local_onboarding_flow_passed=1.
- Insight: The saved local startup flow was previously trustworthy only after the backend returned; the desktop surface showed `Working...` but skipped the visible `starting` state entirely. Moving the in-flight state into `useRuntimeManager` closes that UX gap and makes the status card/drawer reflect the real lifecycle immediately. The onboarding smoke also proves the unified drawer now covers browse -> auto-select -> save -> start without falling back to separate legacy controls.
- Next: keep the lifecycle segment focused on truthfulness after startup, especially periodic `/health` revalidation that can demote a stale `ready` process into `degraded` when the child stops serving correctly.
### Run 45: revalidate managed local health after startup and surface degraded state - runtime_lifecycle_score=100.0 (KEEP)
- Timestamp: 2026-03-27 03:11
- What changed: Added a degraded-state lifecycle check to `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx`, extended `experiments/bench_tauri_runtime_surface.py` with `health_revalidation_passed`, and updated `tauri-app/src-tauri/src/runtime/health.rs` plus `tauri-app/src-tauri/src/runtime/mod.rs` so a running managed local child is periodically revalidated via `/health`. The runtime now demotes stale `ready` state to `degraded` when the process is still alive but no longer serves a healthy endpoint, and clears the `health_check_failed` error when health recovers. Added Rust tests covering both the degrade and recovery paths.
- Result: runtime_lifecycle_score=100.0, unit_passed=1, build_passed=1, cargo_check_passed=1, local_mode_cta_passed=1, saved_startup_flow_passed=1, local_onboarding_flow_passed=1, health_revalidation_passed=1.
- Insight: The frontend surface was already honest once it received a degraded snapshot; the real lifecycle gap was that backend runtime state could stay stuck at `ready` forever as long as the child process had not exited. Revalidating `/health` inside runtime state refresh closes that truthfulness gap without reintroducing a second frontend control plane.
- Next: this lifecycle segment is saturated again at `100.0%`. The next worthwhile move should raise the bar beyond state truthfulness, likely around a higher-fidelity desktop integration check or a real managed-runtime smoke path against an actual local model process.
## Segment 20: localllm Batch Assert
- Objective: stop the local `localllm` server from crashing on first chat completion with `GGML_ASSERT(n_tokens_all <= cparams.n_batch)`.
- Benchmark: `./autoresearch.sh` now runs a focused prompt-prefill regression harness in `experiments/bench_localllm_prefill_batch.py`.
- Keep/discard rule: higher `prefill_batch_assert_score` wins while the targeted prompt-prefill checks, `cargo check`, and a real `LOCALLLM_BATCH=128` completion remain green.
## Segment 21: Tauri Chat Active History
- Objective: make a newly sent message create/select its chat immediately, keep the live generation attached to that chat, and preserve history when revisiting generated chats.
- Benchmark: `./autoresearch.sh` now runs focused frontend regressions in `src/App.test.tsx` and `src/components/ChatView.test.tsx` plus `npm run build`.
- Keep/discard rule: higher `chat_history_score` wins while both the app-level send/create/history flow and the stale-request abort guard remain green.
### Run 47: preserve in-flight generation when chatId switches to the newly created chat - chat_history_score=100.0 (KEEP)
- Timestamp: 2026-03-28 20:06
- What changed: Added an app-level regression for sending from the empty composer into a brand-new chat, then updated `tauri-app/src/components/ChatView.tsx` to track the pending request chat id so `chatId` changes only abort when the user truly switches away to another chat, not when the composer promotes `null` into the just-created chat.
- Result: chat_history_score=100.0, app_send_flow_passed=1, chat_switch_abort_guard_passed=1, frontend_tests_passed=1, frontend_build_passed=1.
- Insight: The previous saved-chat race fix was directionally right but too broad. `null -> newly created chat id` is part of the same send flow, not a user navigation away from the active request.
- Next: if more reports appear, raise the bar with a browser/E2E smoke that sends a message, switches chats mid-stream, and returns to confirm persisted history from the rendered app shell.
- Initial hypothesis: `crates/inference-core/src/session.rs` still uses `llama_batch_get_one()` for full prompt prefill, which likely requests logits for every prompt token. Newer llama.cpp output-buffer semantics appear to need an explicit batch that only asks for logits on the final prompt token and splits larger prompts by `n_batch`.
- Baseline status: pending first red run.
### Run 46: chunk prompt prefill explicitly and fix live batch metadata - prefill_batch_assert_score=100.0 (KEEP)
- Timestamp: 2026-03-27 15:08
- What changed: Replaced full-prompt prefill in `crates/inference-core/src/session.rs` with an explicit chunked `llama_batch_init()` path that only requests logits for the final prompt token and splits prompts by `n_batch`. Passed `n_batch` from `crates/inference-core/src/engine.rs`, bound `llama_get_memory` / `llama_memory_clear` in `crates/llama-sys/src/lib.rs`, and cleared context memory after warmup so the live repro started from a clean state. The decisive live fix was also wiring `batch.n_tokens` correctly before each `llama_decode()` call.
- Result: prefill_batch_assert_score=100.0, prefill_plan_passed=1, cargo_check_passed=1, server_build_passed=1, live_completion_passed=1.
- Insight: The original GGML assert came from the old shorthand batch path over-requesting prompt outputs, but the first explicit prefill implementation still failed live because `llama_batch_init()` leaves `n_tokens` uninitialized. Once the prefill batch both requested only the final prompt logit and reported a valid token count, the real `LOCALLLM_BATCH=128` completion succeeded against the local Qwen GGUF without crashing or returning `generation_failed`.
- Next: keep this branch state and consider a follow-up hardening segment around longer multi-chunk prompts, multi-sequence compatibility, or a regression harness that distinguishes warmup-only startup success from first-request decode success.