synapse-orch-ai 1.6.4 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend/core/config.py +11 -0
- package/backend/core/llm_providers.py +192 -1
- package/backend/core/models.py +7 -0
- package/backend/core/orchestration/steps.py +85 -13
- package/backend/core/routes/data.py +23 -2
- package/backend/core/routes/settings.py +9 -0
- package/backend/core/server.py +14 -0
- package/frontend-build/.next/BUILD_ID +1 -1
- package/frontend-build/.next/build-manifest.json +3 -3
- package/frontend-build/.next/prerender-manifest.json +3 -3
- package/frontend-build/.next/server/app/_global-error.html +1 -1
- package/frontend-build/.next/server/app/_global-error.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/frontend-build/.next/server/app/_not-found.html +1 -1
- package/frontend-build/.next/server/app/_not-found.rsc +2 -2
- package/frontend-build/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/frontend-build/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/frontend-build/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/frontend-build/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/frontend-build/.next/server/app/index.html +1 -1
- package/frontend-build/.next/server/app/index.rsc +2 -2
- package/frontend-build/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/frontend-build/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/frontend-build/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/frontend-build/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/frontend-build/.next/server/app/login.html +1 -1
- package/frontend-build/.next/server/app/login.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/frontend-build/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/frontend-build/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
- package/frontend-build/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/frontend-build/.next/server/app/page_client-reference-manifest.js +1 -1
- package/frontend-build/.next/server/app/settings/[tab]/page_client-reference-manifest.js +1 -1
- package/frontend-build/.next/server/chunks/ssr/_0b~n.nn._.js +27 -27
- package/frontend-build/.next/server/middleware-build-manifest.js +3 -3
- package/frontend-build/.next/server/middleware-manifest.json +5 -5
- package/frontend-build/.next/server/pages/404.html +1 -1
- package/frontend-build/.next/server/pages/500.html +1 -1
- package/frontend-build/.next/server/server-reference-manifest.js +1 -1
- package/frontend-build/.next/server/server-reference-manifest.json +1 -1
- package/frontend-build/.next/static/chunks/06ydx~dmu5o7z.css +1 -0
- package/frontend-build/.next/static/chunks/0_8po4-o2i03b.js +128 -0
- package/package.json +1 -1
- package/frontend-build/.next/static/chunks/0xjv-b-~qzwt1.js +0 -128
- package/frontend-build/.next/static/chunks/0zsell.3txhrd.css +0 -1
- /package/frontend-build/.next/static/{KmMiWG6kMRXgyyjASDx-7 → m4R7kM-i8HghwNcddY-ZF}/_buildManifest.js +0 -0
- /package/frontend-build/.next/static/{KmMiWG6kMRXgyyjASDx-7 → m4R7kM-i8HghwNcddY-ZF}/_clientMiddlewareManifest.js +0 -0
- /package/frontend-build/.next/static/{KmMiWG6kMRXgyyjASDx-7 → m4R7kM-i8HghwNcddY-ZF}/_ssgManifest.js +0 -0
package/backend/core/config.py
CHANGED
|
@@ -36,6 +36,9 @@ def load_settings():
|
|
|
36
36
|
"local_compatible_models": "",
|
|
37
37
|
"openai_compatible_embed_models": "",
|
|
38
38
|
"local_compatible_embed_models": "",
|
|
39
|
+
"huggingface_token": "",
|
|
40
|
+
"huggingface_models": "",
|
|
41
|
+
"huggingface_max_new_tokens": 1024,
|
|
39
42
|
"bedrock_api_key": "",
|
|
40
43
|
"bedrock_inference_profile": "",
|
|
41
44
|
"embedding_model": "",
|
|
@@ -57,6 +60,14 @@ def load_settings():
|
|
|
57
60
|
# ~50–80% cost reduction on multi-turn agents at the cost of a 25% write
|
|
58
61
|
# surcharge on the first turn. Disable only if a provider misbehaves.
|
|
59
62
|
"prompt_cache_enabled": True,
|
|
63
|
+
# Transform step Python execution runtime.
|
|
64
|
+
# "docker" (default): runs in the sandbox-python container — 512 MB / 1 CPU /
|
|
65
|
+
# 60s, isolated from the host.
|
|
66
|
+
# "host": runs as a subprocess on the host with full RAM, GPU, filesystem,
|
|
67
|
+
# and network access. Required for HuggingFace / RecursiveMAS workflows that
|
|
68
|
+
# need torch + GPU but removes the sandbox security boundary. Self-hosted
|
|
69
|
+
# single-user deployments only.
|
|
70
|
+
"transform_runtime": "docker",
|
|
60
71
|
"allow_db_write": False,
|
|
61
72
|
"coding_agent_enabled": True,
|
|
62
73
|
"report_agent_enabled": True,
|
|
@@ -125,6 +125,8 @@ def detect_mode_from_model(model_name: str) -> str:
|
|
|
125
125
|
return "local"
|
|
126
126
|
if m.startswith("ollama."):
|
|
127
127
|
return "local"
|
|
128
|
+
if m.startswith("hf."):
|
|
129
|
+
return "hf"
|
|
128
130
|
return "local"
|
|
129
131
|
|
|
130
132
|
|
|
@@ -162,6 +164,8 @@ def detect_provider_from_model(model_name: str) -> str:
|
|
|
162
164
|
return "local_compatible"
|
|
163
165
|
if m.startswith("ollama."):
|
|
164
166
|
return "ollama"
|
|
167
|
+
if m.startswith("hf."):
|
|
168
|
+
return "huggingface"
|
|
165
169
|
return "ollama"
|
|
166
170
|
|
|
167
171
|
|
|
@@ -1656,6 +1660,174 @@ async def call_bedrock(model_id, messages, system, region, settings, images=None
|
|
|
1656
1660
|
raise LLMError(error_msg)
|
|
1657
1661
|
|
|
1658
1662
|
|
|
1663
|
+
# ─── HuggingFace local-model provider ───────────────────────────────────────────
|
|
1664
|
+
#
|
|
1665
|
+
# Loads transformers models in-process and runs inference on the host's GPU/CPU.
|
|
1666
|
+
# Used for `hf.<org>/<model>` model names (e.g. `hf.Qwen/Qwen2.5-7B-Instruct`).
|
|
1667
|
+
# Models are kept warm in a process-level cache so the 20-60s load cost is paid
|
|
1668
|
+
# once per model, not per call. torch + transformers are lazy-imported so users
|
|
1669
|
+
# who never use hf.* models don't need them installed.
|
|
1670
|
+
|
|
1671
|
+
_hf_model_cache: dict[str, tuple] = {}
|
|
1672
|
+
_hf_cache_lock = threading.Lock()
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
def _hf_device() -> str:
|
|
1676
|
+
"""Best available device for HF models: cuda > mps > cpu."""
|
|
1677
|
+
try:
|
|
1678
|
+
import torch
|
|
1679
|
+
if torch.cuda.is_available():
|
|
1680
|
+
return "cuda"
|
|
1681
|
+
if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
|
|
1682
|
+
return "mps"
|
|
1683
|
+
except Exception:
|
|
1684
|
+
pass
|
|
1685
|
+
return "cpu"
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
def _load_hf_model(model_id: str, hf_token: str | None = None):
|
|
1689
|
+
"""Load (or fetch from cache) an HF tokenizer + model pair.
|
|
1690
|
+
|
|
1691
|
+
Lock-guarded so concurrent calls don't double-load the same model.
|
|
1692
|
+
"""
|
|
1693
|
+
with _hf_cache_lock:
|
|
1694
|
+
if model_id in _hf_model_cache:
|
|
1695
|
+
return _hf_model_cache[model_id]
|
|
1696
|
+
|
|
1697
|
+
try:
|
|
1698
|
+
import torch
|
|
1699
|
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
1700
|
+
except ImportError as e:
|
|
1701
|
+
raise LLMError(
|
|
1702
|
+
"HuggingFace provider: 'torch' and 'transformers' are not installed. "
|
|
1703
|
+
"Install them on the Synapse host (e.g. `pip install torch transformers`) "
|
|
1704
|
+
"to use hf.* models."
|
|
1705
|
+
) from e
|
|
1706
|
+
|
|
1707
|
+
device = _hf_device()
|
|
1708
|
+
dtype = torch.float16 if device in ("cuda", "mps") else torch.float32
|
|
1709
|
+
load_kwargs: dict = {"torch_dtype": dtype}
|
|
1710
|
+
if device == "cuda":
|
|
1711
|
+
load_kwargs["device_map"] = "auto"
|
|
1712
|
+
if hf_token:
|
|
1713
|
+
load_kwargs["token"] = hf_token
|
|
1714
|
+
|
|
1715
|
+
print(f"DEBUG: 🤗 Loading HF model '{model_id}' on {device} — first call pays 20-60s load cost...", flush=True)
|
|
1716
|
+
|
|
1717
|
+
tok_kwargs: dict = {"token": hf_token} if hf_token else {}
|
|
1718
|
+
tokenizer = AutoTokenizer.from_pretrained(model_id, **tok_kwargs)
|
|
1719
|
+
model = AutoModelForCausalLM.from_pretrained(model_id, **load_kwargs)
|
|
1720
|
+
|
|
1721
|
+
# device_map="auto" already places shards; only move when we didn't use it.
|
|
1722
|
+
if device != "cuda":
|
|
1723
|
+
model = model.to(device)
|
|
1724
|
+
model.eval()
|
|
1725
|
+
|
|
1726
|
+
_hf_model_cache[model_id] = (tokenizer, model)
|
|
1727
|
+
print(f"DEBUG: ✅ HF model '{model_id}' loaded and cached.", flush=True)
|
|
1728
|
+
return tokenizer, model
|
|
1729
|
+
|
|
1730
|
+
|
|
1731
|
+
async def call_huggingface(
|
|
1732
|
+
model: str,
|
|
1733
|
+
messages: list[dict],
|
|
1734
|
+
system: str,
|
|
1735
|
+
settings: dict,
|
|
1736
|
+
tools: list[dict] | None = None,
|
|
1737
|
+
images: list[str] | None = None,
|
|
1738
|
+
):
|
|
1739
|
+
"""Run inference on a local HuggingFace transformers model.
|
|
1740
|
+
|
|
1741
|
+
Args:
|
|
1742
|
+
model: Full model name with hf. prefix (e.g. 'hf.Qwen/Qwen2.5-7B-Instruct').
|
|
1743
|
+
messages: OpenAI-style [{"role": ..., "content": ...}] list.
|
|
1744
|
+
system: System instruction text.
|
|
1745
|
+
settings: User settings dict. Reads `huggingface_token` (for gated models)
|
|
1746
|
+
and `huggingface_max_new_tokens` (default 1024).
|
|
1747
|
+
tools: Ignored with a warning — native tool calling varies by model and
|
|
1748
|
+
is left to the caller to inject via prompt text if needed.
|
|
1749
|
+
images: Ignored — vision is model-specific and not supported in this MVP.
|
|
1750
|
+
|
|
1751
|
+
Returns:
|
|
1752
|
+
(response_text, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens)
|
|
1753
|
+
— cache fields are always 0 (no cross-call prompt caching for local inference).
|
|
1754
|
+
"""
|
|
1755
|
+
model_id = model[len("hf."):] if model.startswith("hf.") else model
|
|
1756
|
+
if not model_id:
|
|
1757
|
+
raise LLMError("HuggingFace provider: empty model name after 'hf.' prefix.")
|
|
1758
|
+
|
|
1759
|
+
if tools:
|
|
1760
|
+
print(
|
|
1761
|
+
f"DEBUG: ⚠️ HF provider received {len(tools)} tool(s) but does not support "
|
|
1762
|
+
f"native tool calling. Inject tool descriptions into the system prompt if needed.",
|
|
1763
|
+
flush=True,
|
|
1764
|
+
)
|
|
1765
|
+
if images:
|
|
1766
|
+
print(
|
|
1767
|
+
f"DEBUG: ⚠️ HF provider received {len(images)} image(s) but vision is not "
|
|
1768
|
+
f"supported in this MVP — they will be dropped.",
|
|
1769
|
+
flush=True,
|
|
1770
|
+
)
|
|
1771
|
+
|
|
1772
|
+
hf_token = (settings.get("huggingface_token") or "").strip() or None
|
|
1773
|
+
max_new_tokens = int(settings.get("huggingface_max_new_tokens") or 1024)
|
|
1774
|
+
|
|
1775
|
+
def _run():
|
|
1776
|
+
import torch
|
|
1777
|
+
tokenizer, model_obj = _load_hf_model(model_id, hf_token)
|
|
1778
|
+
|
|
1779
|
+
chat_messages: list[dict] = []
|
|
1780
|
+
if system and system.strip():
|
|
1781
|
+
chat_messages.append({"role": "system", "content": system.strip()})
|
|
1782
|
+
chat_messages.extend(messages or [])
|
|
1783
|
+
|
|
1784
|
+
# Prefer the model's chat template; fall back to a plain transcript if
|
|
1785
|
+
# the tokenizer doesn't define one (some base / non-instruct models).
|
|
1786
|
+
try:
|
|
1787
|
+
input_ids = tokenizer.apply_chat_template(
|
|
1788
|
+
chat_messages,
|
|
1789
|
+
add_generation_prompt=True,
|
|
1790
|
+
return_tensors="pt",
|
|
1791
|
+
)
|
|
1792
|
+
except Exception as e:
|
|
1793
|
+
print(f"DEBUG: ⚠️ HF chat template unavailable ({e}); using transcript fallback.", flush=True)
|
|
1794
|
+
transcript_parts: list[str] = []
|
|
1795
|
+
if system and system.strip():
|
|
1796
|
+
transcript_parts.append(f"System: {system.strip()}")
|
|
1797
|
+
t = _messages_to_transcript(messages)
|
|
1798
|
+
if t:
|
|
1799
|
+
transcript_parts.append(t)
|
|
1800
|
+
transcript_parts.append("Assistant:")
|
|
1801
|
+
transcript = "\n\n".join(transcript_parts)
|
|
1802
|
+
input_ids = tokenizer(transcript, return_tensors="pt").input_ids
|
|
1803
|
+
|
|
1804
|
+
input_ids = input_ids.to(model_obj.device)
|
|
1805
|
+
prompt_token_count = int(input_ids.shape[-1])
|
|
1806
|
+
|
|
1807
|
+
with torch.no_grad():
|
|
1808
|
+
output_ids = model_obj.generate(
|
|
1809
|
+
input_ids,
|
|
1810
|
+
max_new_tokens=max_new_tokens,
|
|
1811
|
+
do_sample=False,
|
|
1812
|
+
pad_token_id=tokenizer.eos_token_id,
|
|
1813
|
+
)
|
|
1814
|
+
|
|
1815
|
+
new_tokens = output_ids[0, prompt_token_count:]
|
|
1816
|
+
text = tokenizer.decode(new_tokens, skip_special_tokens=True).strip()
|
|
1817
|
+
output_token_count = int(new_tokens.shape[-1])
|
|
1818
|
+
return text, prompt_token_count, output_token_count
|
|
1819
|
+
|
|
1820
|
+
try:
|
|
1821
|
+
print(f"DEBUG: 🔄 HF call start (model={model_id})", flush=True)
|
|
1822
|
+
text, input_tokens, output_tokens = await asyncio.to_thread(_run)
|
|
1823
|
+
print(f"DEBUG: ✅ HF call complete (model={model_id}, out_tokens={output_tokens})", flush=True)
|
|
1824
|
+
return text, input_tokens, output_tokens, 0, 0
|
|
1825
|
+
except LLMError:
|
|
1826
|
+
raise
|
|
1827
|
+
except Exception as e:
|
|
1828
|
+
raise LLMError(f"HuggingFace provider error ({model_id}): {e}")
|
|
1829
|
+
|
|
1830
|
+
|
|
1659
1831
|
def _messages_to_transcript(messages: list[dict] | None) -> str:
|
|
1660
1832
|
"""Lossy conversion of role/content messages to plain text for providers that only accept a single prompt."""
|
|
1661
1833
|
if not messages:
|
|
@@ -1930,7 +2102,26 @@ async def generate_response(
|
|
|
1930
2102
|
except Exception as e:
|
|
1931
2103
|
return f"CLI Provider Error: {str(e)}"
|
|
1932
2104
|
|
|
1933
|
-
|
|
2105
|
+
elif mode == "hf":
|
|
2106
|
+
try:
|
|
2107
|
+
messages_for_hf = []
|
|
2108
|
+
if history_messages:
|
|
2109
|
+
messages_for_hf.extend(history_messages)
|
|
2110
|
+
messages_for_hf.append({"role": "user", "content": prompt_msg})
|
|
2111
|
+
|
|
2112
|
+
result_text, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens = await call_huggingface(
|
|
2113
|
+
current_model,
|
|
2114
|
+
messages_for_hf,
|
|
2115
|
+
augmented_system,
|
|
2116
|
+
current_settings,
|
|
2117
|
+
tools=tools,
|
|
2118
|
+
images=images,
|
|
2119
|
+
)
|
|
2120
|
+
except LLMError:
|
|
2121
|
+
raise
|
|
2122
|
+
except Exception as e:
|
|
2123
|
+
return f"HuggingFace Provider Error: {str(e)}"
|
|
2124
|
+
|
|
1934
2125
|
else:
|
|
1935
2126
|
# Local Ollama
|
|
1936
2127
|
# Strip the ollama. prefix — the Ollama API expects bare model names
|
package/backend/core/models.py
CHANGED
|
@@ -88,6 +88,13 @@ class Settings(BaseModel):
|
|
|
88
88
|
local_compatible_models: str = "" # Comma-separated model names for local v1-compatible
|
|
89
89
|
openai_compatible_embed_models: str = "" # Comma-separated embedding model names for OpenAI-compatible cloud
|
|
90
90
|
local_compatible_embed_models: str = "" # Comma-separated embedding model names for local v1-compatible
|
|
91
|
+
huggingface_token: str = "" # Optional HF access token (required for gated models like Llama, Gemma)
|
|
92
|
+
huggingface_models: str = "" # Comma- or newline-separated HF model IDs (e.g. Qwen/Qwen2.5-7B-Instruct)
|
|
93
|
+
huggingface_max_new_tokens: int = 1024 # Max tokens to generate per HF call
|
|
94
|
+
# Transform step Python execution runtime: "docker" (sandboxed, default) or
|
|
95
|
+
# "host" (unsandboxed subprocess on host — needed for torch/GPU workloads,
|
|
96
|
+
# removes the sandbox security boundary).
|
|
97
|
+
transform_runtime: str = "docker"
|
|
91
98
|
bedrock_api_key: str = "" # e.g. ABSK... (Amazon Bedrock API key)
|
|
92
99
|
# Optional: required for some Bedrock models that don't support on-demand throughput.
|
|
93
100
|
# Can be an inference profile ID or full ARN.
|
|
@@ -5,6 +5,7 @@ Each executor is an async generator that yields SSE-compatible events.
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import datetime
|
|
7
7
|
import json
|
|
8
|
+
import os
|
|
8
9
|
import re
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
@@ -1098,7 +1099,12 @@ class TransformStepExecutor:
|
|
|
1098
1099
|
yield {"type": "_log_prompt", "orch_step_id": step.id, "prompt": f"[Transform Code]\n{code}"}
|
|
1099
1100
|
|
|
1100
1101
|
try:
|
|
1101
|
-
|
|
1102
|
+
from core.config import load_settings
|
|
1103
|
+
runtime = (load_settings().get("transform_runtime") or "docker").strip().lower()
|
|
1104
|
+
if runtime == "host":
|
|
1105
|
+
result = await self._run_host(code, run.shared_state, timeout=step.timeout_seconds)
|
|
1106
|
+
else:
|
|
1107
|
+
result = await self._run_sandboxed(code, run.shared_state, timeout=step.timeout_seconds)
|
|
1102
1108
|
|
|
1103
1109
|
if step.output_key and result is not None:
|
|
1104
1110
|
run.shared_state[step.output_key] = result
|
|
@@ -1111,6 +1117,82 @@ class TransformStepExecutor:
|
|
|
1111
1117
|
except Exception as e:
|
|
1112
1118
|
yield {"type": "step_error", "orch_step_id": step.id, "error": f"Transform error: {e}"}
|
|
1113
1119
|
|
|
1120
|
+
def _build_script(self, code: str, state: dict) -> str:
|
|
1121
|
+
"""Build the JSON-in/JSON-out script wrapper used by both runtimes."""
|
|
1122
|
+
state_json = json.dumps(state, default=str)
|
|
1123
|
+
escaped = state_json.replace("\\", "\\\\").replace('"""', '\\"\\"\\"')
|
|
1124
|
+
return (
|
|
1125
|
+
f'import json\nstate = json.loads("""{escaped}""")\nresult = None\n\n'
|
|
1126
|
+
+ code
|
|
1127
|
+
+ '\n\nif result is not None:\n print(json.dumps({"result": result}, default=str))\nelse:\n print(json.dumps({"result": None}))\n'
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
def _parse_script_output(self, stdout_text: str):
|
|
1131
|
+
"""Parse the JSON output of the wrapped script. Falls back to plain text."""
|
|
1132
|
+
try:
|
|
1133
|
+
output = json.loads(stdout_text)
|
|
1134
|
+
return output.get("result")
|
|
1135
|
+
except json.JSONDecodeError:
|
|
1136
|
+
return stdout_text.strip() if stdout_text.strip() else None
|
|
1137
|
+
|
|
1138
|
+
async def _run_host(self, code: str, state: dict, timeout: int = 30):
|
|
1139
|
+
"""Run Python code directly on the host via subprocess (NO sandbox).
|
|
1140
|
+
|
|
1141
|
+
Used when transform_runtime == "host". Gives the script full access to
|
|
1142
|
+
the host's Python env, GPU, filesystem, and network. Required for
|
|
1143
|
+
torch/transformers workloads that can't run in the 512MB Docker sandbox.
|
|
1144
|
+
|
|
1145
|
+
Same JSON-in/JSON-out contract as the docker path. The script inherits
|
|
1146
|
+
the backend process env (so CUDA_VISIBLE_DEVICES, HF_HOME, etc. flow
|
|
1147
|
+
through naturally) and runs under the same Python interpreter as the
|
|
1148
|
+
backend (sys.executable).
|
|
1149
|
+
|
|
1150
|
+
Timeout: caps at 30 minutes to bound runaway loads/inference. The docker
|
|
1151
|
+
path's 60s cap is too low for model loading, so we deliberately lift it
|
|
1152
|
+
here. Step-level timeout_seconds still applies.
|
|
1153
|
+
"""
|
|
1154
|
+
import sys
|
|
1155
|
+
import shutil
|
|
1156
|
+
import tempfile
|
|
1157
|
+
from pathlib import Path
|
|
1158
|
+
|
|
1159
|
+
script_content = self._build_script(code, state)
|
|
1160
|
+
effective_timeout = min(max(int(timeout or 30), 1), 1800)
|
|
1161
|
+
|
|
1162
|
+
tmp_dir = tempfile.mkdtemp(prefix="transform_host_")
|
|
1163
|
+
script_path = str(Path(tmp_dir) / "transform.py")
|
|
1164
|
+
try:
|
|
1165
|
+
with open(script_path, "w") as f:
|
|
1166
|
+
f.write(script_content)
|
|
1167
|
+
|
|
1168
|
+
proc = await asyncio.create_subprocess_exec(
|
|
1169
|
+
sys.executable, script_path,
|
|
1170
|
+
stdout=asyncio.subprocess.PIPE,
|
|
1171
|
+
stderr=asyncio.subprocess.PIPE,
|
|
1172
|
+
env=os.environ.copy(),
|
|
1173
|
+
)
|
|
1174
|
+
try:
|
|
1175
|
+
stdout_b, stderr_b = await asyncio.wait_for(
|
|
1176
|
+
proc.communicate(), timeout=effective_timeout + 5
|
|
1177
|
+
)
|
|
1178
|
+
except asyncio.TimeoutError:
|
|
1179
|
+
try:
|
|
1180
|
+
proc.kill()
|
|
1181
|
+
await proc.wait()
|
|
1182
|
+
except Exception:
|
|
1183
|
+
pass
|
|
1184
|
+
raise RuntimeError(f"Transform (host) timed out after {effective_timeout}s")
|
|
1185
|
+
|
|
1186
|
+
stdout_text = stdout_b.decode("utf-8", errors="replace")
|
|
1187
|
+
stderr_text = stderr_b.decode("utf-8", errors="replace")
|
|
1188
|
+
|
|
1189
|
+
if proc.returncode != 0:
|
|
1190
|
+
raise RuntimeError(f"Transform (host) failed (exit {proc.returncode}): {stderr_text[:500]}")
|
|
1191
|
+
|
|
1192
|
+
return self._parse_script_output(stdout_text)
|
|
1193
|
+
finally:
|
|
1194
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
1195
|
+
|
|
1114
1196
|
async def _run_sandboxed(self, code: str, state: dict, timeout: int = 30):
|
|
1115
1197
|
"""Run Python code in the Docker sandbox (sandbox-python:latest)."""
|
|
1116
1198
|
import shutil
|
|
@@ -1120,13 +1202,7 @@ class TransformStepExecutor:
|
|
|
1120
1202
|
if not shutil.which("docker"):
|
|
1121
1203
|
raise RuntimeError("Docker is not available. Cannot execute transform code in sandbox.")
|
|
1122
1204
|
|
|
1123
|
-
|
|
1124
|
-
escaped = state_json.replace("\\", "\\\\").replace('"""', '\\"\\"\\"')
|
|
1125
|
-
script_content = (
|
|
1126
|
-
f'import json\nstate = json.loads("""{escaped}""")\nresult = None\n\n'
|
|
1127
|
-
+ code
|
|
1128
|
-
+ '\n\nif result is not None:\n print(json.dumps({"result": result}, default=str))\nelse:\n print(json.dumps({"result": None}))\n'
|
|
1129
|
-
)
|
|
1205
|
+
script_content = self._build_script(code, state)
|
|
1130
1206
|
|
|
1131
1207
|
DATA_DIR_PATH = Path(__file__).resolve().parent.parent.parent / "data"
|
|
1132
1208
|
vault_root = DATA_DIR_PATH / "vault"
|
|
@@ -1172,11 +1248,7 @@ class TransformStepExecutor:
|
|
|
1172
1248
|
if proc.returncode != 0:
|
|
1173
1249
|
raise RuntimeError(f"Transform failed (exit {proc.returncode}): {stderr_text[:500]}")
|
|
1174
1250
|
|
|
1175
|
-
|
|
1176
|
-
output = json.loads(stdout_text)
|
|
1177
|
-
return output.get("result")
|
|
1178
|
-
except json.JSONDecodeError:
|
|
1179
|
-
return stdout_text.strip() if stdout_text.strip() else None
|
|
1251
|
+
return self._parse_script_output(stdout_text)
|
|
1180
1252
|
finally:
|
|
1181
1253
|
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
1182
1254
|
|
|
@@ -190,6 +190,7 @@ async def get_models():
|
|
|
190
190
|
local_compatible_models_str = (settings.get("local_compatible_models") or "").strip()
|
|
191
191
|
openai_compatible_embed_models_str = (settings.get("openai_compatible_embed_models") or "").strip()
|
|
192
192
|
local_compatible_embed_models_str = (settings.get("local_compatible_embed_models") or "").strip()
|
|
193
|
+
huggingface_models_str = (settings.get("huggingface_models") or "").strip()
|
|
193
194
|
bedrock_available = bool((settings.get("bedrock_api_key") or "").strip() or
|
|
194
195
|
(settings.get("aws_access_key_id") or "").strip())
|
|
195
196
|
|
|
@@ -466,6 +467,22 @@ async def get_models():
|
|
|
466
467
|
prefixed_embed = [f"locv1.{m}" for m in all_embed]
|
|
467
468
|
return available, prefixed, prefixed_embed
|
|
468
469
|
|
|
470
|
+
async def fetch_huggingface() -> tuple[bool, list[str], list[str]]:
|
|
471
|
+
"""List user-configured HuggingFace model IDs, prefixed with hf.
|
|
472
|
+
|
|
473
|
+
No API discovery — HF model namespace is enormous and not enumerable.
|
|
474
|
+
The user lists the specific model IDs they want available (one per line
|
|
475
|
+
or comma-separated) in Settings → Models → HuggingFace Models.
|
|
476
|
+
"""
|
|
477
|
+
models = [
|
|
478
|
+
m.strip() for m in huggingface_models_str.replace("\n", ",").split(",")
|
|
479
|
+
if m.strip()
|
|
480
|
+
]
|
|
481
|
+
if not models:
|
|
482
|
+
return False, [], []
|
|
483
|
+
prefixed = [f"hf.{m}" if not m.startswith("hf.") else m for m in models]
|
|
484
|
+
return True, prefixed, []
|
|
485
|
+
|
|
469
486
|
# Run all fetches concurrently; return_exceptions=True ensures one provider failure
|
|
470
487
|
# doesn't cancel the others.
|
|
471
488
|
_PROVIDER_FALLBACKS = [
|
|
@@ -482,8 +499,9 @@ async def get_models():
|
|
|
482
499
|
(False, [], []), # gemini_cli
|
|
483
500
|
(False, [], []), # codex_cli
|
|
484
501
|
(False, [], []), # github_copilot_cli
|
|
502
|
+
(False, [], []), # huggingface
|
|
485
503
|
]
|
|
486
|
-
_PROVIDER_NAMES = ["ollama", "openai", "anthropic", "gemini", "grok", "deepseek", "bedrock", "openai_compatible", "local_compatible", "anthropic_cli", "gemini_cli", "codex_cli", "github_copilot_cli"]
|
|
504
|
+
_PROVIDER_NAMES = ["ollama", "openai", "anthropic", "gemini", "grok", "deepseek", "bedrock", "openai_compatible", "local_compatible", "anthropic_cli", "gemini_cli", "codex_cli", "github_copilot_cli", "huggingface"]
|
|
487
505
|
|
|
488
506
|
raw = await asyncio.gather(
|
|
489
507
|
fetch_ollama(), fetch_openai(), fetch_anthropic(),
|
|
@@ -491,6 +509,7 @@ async def get_models():
|
|
|
491
509
|
fetch_openai_compatible(), fetch_local_compatible(),
|
|
492
510
|
fetch_claude_cli(), fetch_gemini_cli(), fetch_codex_cli(),
|
|
493
511
|
fetch_github_copilot_cli(),
|
|
512
|
+
fetch_huggingface(),
|
|
494
513
|
return_exceptions=True,
|
|
495
514
|
)
|
|
496
515
|
|
|
@@ -515,6 +534,7 @@ async def get_models():
|
|
|
515
534
|
c_gemini_avail, c_gemini_chat, _ = results[10]
|
|
516
535
|
c_codex_avail, c_codex_chat, _ = results[11]
|
|
517
536
|
c_copilot_avail, c_copilot_chat, _ = results[12]
|
|
537
|
+
hf_avail, hf_chat, _ = results[13]
|
|
518
538
|
|
|
519
539
|
# --- Build provider map ---
|
|
520
540
|
providers = {
|
|
@@ -531,6 +551,7 @@ async def get_models():
|
|
|
531
551
|
"gemini_cli": {"available": c_gemini_avail, "models": c_gemini_chat, "embedding_models": []},
|
|
532
552
|
"codex_cli": {"available": c_codex_avail, "models": c_codex_chat, "embedding_models": []},
|
|
533
553
|
"github_copilot_cli": {"available": c_copilot_avail, "models": c_copilot_chat, "embedding_models": []},
|
|
554
|
+
"huggingface": {"available": hf_avail, "models": hf_chat, "embedding_models": []},
|
|
534
555
|
}
|
|
535
556
|
|
|
536
557
|
# --- Flat list of all available models ---
|
|
@@ -540,7 +561,7 @@ async def get_models():
|
|
|
540
561
|
all_available.extend(info["models"])
|
|
541
562
|
|
|
542
563
|
# --- Backward compat ---
|
|
543
|
-
cloud_models = gemini_chat + anthropic_chat + openai_chat + grok_chat + deepseek_chat + BEDROCK_FALLBACK + oaic_chat + locv1_chat + c_claude_chat + c_gemini_chat + c_codex_chat + c_copilot_chat
|
|
564
|
+
cloud_models = gemini_chat + anthropic_chat + openai_chat + grok_chat + deepseek_chat + BEDROCK_FALLBACK + oaic_chat + locv1_chat + c_claude_chat + c_gemini_chat + c_codex_chat + c_copilot_chat + hf_chat
|
|
544
565
|
|
|
545
566
|
return {
|
|
546
567
|
"providers": providers,
|
|
@@ -127,6 +127,7 @@ async def update_settings(settings: Settings):
|
|
|
127
127
|
existing = load_settings()
|
|
128
128
|
# Never overwrite the bcrypt hash via the general settings endpoint
|
|
129
129
|
data.pop("login_password_hash", None)
|
|
130
|
+
prev_bash_dirs = list(existing.get("bash_allowed_dirs", []))
|
|
130
131
|
existing.update(data)
|
|
131
132
|
data = existing
|
|
132
133
|
|
|
@@ -134,6 +135,14 @@ async def update_settings(settings: Settings):
|
|
|
134
135
|
|
|
135
136
|
# Reinitialize memory so embeddings provider matches the new mode.
|
|
136
137
|
import core.server as _server
|
|
138
|
+
|
|
139
|
+
# If the user's allowed directories changed, restart the Filesystem MCP
|
|
140
|
+
# server so its allowed roots match what the bash tool sees.
|
|
141
|
+
if list(data.get("bash_allowed_dirs", [])) != prev_bash_dirs:
|
|
142
|
+
try:
|
|
143
|
+
await _server.restart_filesystem_mcp()
|
|
144
|
+
except Exception as e:
|
|
145
|
+
print(f"Warning: Failed to restart filesystem MCP after settings update: {e}")
|
|
137
146
|
try:
|
|
138
147
|
from core.memory import MemoryStore as _MemoryStore
|
|
139
148
|
except ImportError:
|
package/backend/core/server.py
CHANGED
|
@@ -177,6 +177,20 @@ def _build_native_mcp_servers() -> list[dict]:
|
|
|
177
177
|
vault_path = str(DATA_DIR / "vault")
|
|
178
178
|
# Always start with vault; include any configured repo paths on top.
|
|
179
179
|
fs_paths = repo_paths + [vault_path]
|
|
180
|
+
# Also include user-configured directories from General Settings ("Allowed
|
|
181
|
+
# Directories"). Read fresh so a restart picks up edits without a backend
|
|
182
|
+
# reboot. Filter to existing dirs and de-duplicate against paths we already
|
|
183
|
+
# have so the MCP server doesn't see the same root twice.
|
|
184
|
+
extra_dirs = [
|
|
185
|
+
d for d in load_settings().get("bash_allowed_dirs", [])
|
|
186
|
+
if d and os.path.isdir(d)
|
|
187
|
+
]
|
|
188
|
+
seen = {os.path.realpath(p) for p in fs_paths}
|
|
189
|
+
for d in extra_dirs:
|
|
190
|
+
rp = os.path.realpath(d)
|
|
191
|
+
if rp not in seen:
|
|
192
|
+
fs_paths.append(d)
|
|
193
|
+
seen.add(rp)
|
|
180
194
|
if not repo_paths:
|
|
181
195
|
print("Warning: No repos configured — starting filesystem MCP server with vault access only.")
|
|
182
196
|
servers.append({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
m4R7kM-i8HghwNcddY-ZF
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"static/chunks/03~yq9q893hmn.js"
|
|
8
8
|
],
|
|
9
9
|
"lowPriorityFiles": [
|
|
10
|
-
"static/
|
|
11
|
-
"static/
|
|
12
|
-
"static/
|
|
10
|
+
"static/m4R7kM-i8HghwNcddY-ZF/_buildManifest.js",
|
|
11
|
+
"static/m4R7kM-i8HghwNcddY-ZF/_ssgManifest.js",
|
|
12
|
+
"static/m4R7kM-i8HghwNcddY-ZF/_clientMiddlewareManifest.js"
|
|
13
13
|
],
|
|
14
14
|
"rootMainFiles": [
|
|
15
15
|
"static/chunks/0qmh881w772ef.js",
|
|
@@ -131,8 +131,8 @@
|
|
|
131
131
|
"dynamicRoutes": {},
|
|
132
132
|
"notFoundRoutes": [],
|
|
133
133
|
"preview": {
|
|
134
|
-
"previewModeId": "
|
|
135
|
-
"previewModeSigningKey": "
|
|
136
|
-
"previewModeEncryptionKey": "
|
|
134
|
+
"previewModeId": "5c5fb1eb12105ecc31b510ba2637e0be",
|
|
135
|
+
"previewModeSigningKey": "f93f20bb75db2a07fb89b131e299486f7dbb66c3440cc494939742886cb823ee",
|
|
136
|
+
"previewModeEncryptionKey": "274a80622931c9d185f4d42b657245f107072f0e729938b2d5c3cb56ae2dd9b7"
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0qmh881w772ef.js"/><script src="/_next/static/chunks/02i7dfk78~t~2.js" async=""></script><script src="/_next/static/chunks/0c7h~x4_chf35.js" async=""></script><script src="/_next/static/chunks/00xhsd~k-wxkl.js" async=""></script><script src="/_next/static/chunks/turbopack-09lztqg9--eu..js" async=""></script><script src="/_next/static/chunks/01xlw8hd842-c.js" async=""></script><script src="/_next/static/chunks/0m92kh92cfh_..js" async=""></script><meta name="next-size-adjust" content=""/><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/0qmh881w772ef.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[39756,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"default\"]\n3:I[37457,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"default\"]\n4:I[97367,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[97367,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"ViewportBoundary\"]\na:I[97367,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"MetadataBoundary\"]\nc:I[68027,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"
|
|
1
|
+
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0qmh881w772ef.js"/><script src="/_next/static/chunks/02i7dfk78~t~2.js" async=""></script><script src="/_next/static/chunks/0c7h~x4_chf35.js" async=""></script><script src="/_next/static/chunks/00xhsd~k-wxkl.js" async=""></script><script src="/_next/static/chunks/turbopack-09lztqg9--eu..js" async=""></script><script src="/_next/static/chunks/01xlw8hd842-c.js" async=""></script><script src="/_next/static/chunks/0m92kh92cfh_..js" async=""></script><meta name="next-size-adjust" content=""/><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/0qmh881w772ef.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[39756,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"default\"]\n3:I[37457,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"default\"]\n4:I[97367,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[97367,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"ViewportBoundary\"]\na:I[97367,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"MetadataBoundary\"]\nc:I[68027,[\"/_next/static/chunks/01xlw8hd842-c.js\",\"/_next/static/chunks/0m92kh92cfh_..js\"],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"m4R7kM-i8HghwNcddY-ZF\"}\n"])</script><script>self.__next_f.push([1,"d:[]\n7:\"$Wd\"\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\nb:[]\n"])</script></body></html>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
8:I[97367,["/_next/static/chunks/01xlw8hd842-c.js","/_next/static/chunks/0m92kh92cfh_..js"],"ViewportBoundary"]
|
|
7
7
|
a:I[97367,["/_next/static/chunks/01xlw8hd842-c.js","/_next/static/chunks/0m92kh92cfh_..js"],"MetadataBoundary"]
|
|
8
8
|
c:I[68027,["/_next/static/chunks/01xlw8hd842-c.js","/_next/static/chunks/0m92kh92cfh_..js"],"default",1]
|
|
9
|
-
0:{"P":null,"c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":[["$","title",null,{"children":"500: This page couldn’t load"}],["$","style",null,{"dangerouslySetInnerHTML":{"__html":":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }"}}]]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","display":"flex","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"marginTop":"-32px","maxWidth":"325px","padding":"32px 28px","textAlign":"left"},"children":[["$","svg",null,{"width":"32","height":"32","viewBox":"-0.2 -1.5 32 32","fill":"none","style":{"marginBottom":"24px"},"children":["$","path",null,{"d":"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z","fill":"var(--next-error-title)"}]}],["$","h1",null,{"style":{"fontSize":"24px","fontWeight":500,"letterSpacing":"-0.02em","lineHeight":"32px","margin":"0 0 12px 0","color":"var(--next-error-title)"},"children":"This page couldn’t load"}],["$","p",null,{"style":{"fontSize":"14px","fontWeight":400,"lineHeight":"21px","margin":"0 0 20px 0","color":"var(--next-error-message)"},"children":"A server error occurred. Reload to try again."}],["$","form",null,{"style":{"margin":0},"children":["$","button",null,{"type":"submit","style":{"display":"inline-flex","alignItems":"center","justifyContent":"center","height":"32px","padding":"0 12px","fontSize":"14px","fontWeight":500,"lineHeight":"20px","borderRadius":"6px","cursor":"pointer","color":"var(--next-error-btn-text)","background":"var(--next-error-btn-bg)","border":"var(--next-error-btn-border)"},"children":"Reload"}]}]]}]}]}]]}],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,null]},null,false,"$@7"],["$","$1","h",{"children":[null,["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$c",[]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"
|
|
9
|
+
0:{"P":null,"c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":[["$","title",null,{"children":"500: This page couldn’t load"}],["$","style",null,{"dangerouslySetInnerHTML":{"__html":":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }"}}]]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","display":"flex","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"marginTop":"-32px","maxWidth":"325px","padding":"32px 28px","textAlign":"left"},"children":[["$","svg",null,{"width":"32","height":"32","viewBox":"-0.2 -1.5 32 32","fill":"none","style":{"marginBottom":"24px"},"children":["$","path",null,{"d":"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z","fill":"var(--next-error-title)"}]}],["$","h1",null,{"style":{"fontSize":"24px","fontWeight":500,"letterSpacing":"-0.02em","lineHeight":"32px","margin":"0 0 12px 0","color":"var(--next-error-title)"},"children":"This page couldn’t load"}],["$","p",null,{"style":{"fontSize":"14px","fontWeight":400,"lineHeight":"21px","margin":"0 0 20px 0","color":"var(--next-error-message)"},"children":"A server error occurred. Reload to try again."}],["$","form",null,{"style":{"margin":0},"children":["$","button",null,{"type":"submit","style":{"display":"inline-flex","alignItems":"center","justifyContent":"center","height":"32px","padding":"0 12px","fontSize":"14px","fontWeight":500,"lineHeight":"20px","borderRadius":"6px","cursor":"pointer","color":"var(--next-error-btn-text)","background":"var(--next-error-btn-bg)","border":"var(--next-error-btn-border)"},"children":"Reload"}]}]]}]}]}]]}],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,null]},null,false,"$@7"],["$","$1","h",{"children":[null,["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$c",[]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"m4R7kM-i8HghwNcddY-ZF"}
|
|
10
10
|
d:[]
|
|
11
11
|
7:"$Wd"
|
|
12
12
|
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|