ltcai 11.5.1 → 11.5.2
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/README.md +85 -53
- package/docs/CHANGELOG.md +54 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/HYBRID_CLOUD_KG_STREAMING.md +8 -4
- package/docs/MULTI_AGENT_RUNTIME.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/WORKFLOW_DESIGNER.md +1 -1
- package/docs/architecture.md +4 -4
- package/docs/kg-schema.md +1 -1
- package/docs/v11.5.2_TIGHT_SHIP_PLAN.md +144 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/proactive.py +1 -1
- package/lattice_brain/graph/projection/v2_schema.py +0 -28
- package/lattice_brain/graph/vector_index/base.py +0 -3
- package/lattice_brain/graph/vector_index/brute_force.py +0 -4
- package/lattice_brain/graph/vector_index/hnsw.py +0 -3
- package/lattice_brain/graph/vector_index/quantized.py +0 -3
- package/lattice_brain/ingestion_jobs.py +0 -3
- package/lattice_brain/memory.py +0 -27
- package/lattice_brain/portability/sharing.py +0 -4
- package/lattice_brain/quality.py +0 -49
- package/lattice_brain/runtime/agent_runtime.py +1 -1
- package/lattice_brain/runtime/hooks.py +0 -13
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/lattice_brain/sealed_box.py +0 -4
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +12 -12
- package/latticeai/api/auth.py +25 -2
- package/latticeai/api/chat.py +3 -3
- package/latticeai/api/chat_agent_http.py +3 -12
- package/latticeai/api/chat_helpers.py +10 -13
- package/latticeai/api/computer_use.py +31 -42
- package/latticeai/api/health.py +21 -1
- package/latticeai/api/local_files.py +14 -0
- package/latticeai/api/permissions.py +38 -12
- package/latticeai/api/search.py +24 -0
- package/latticeai/api/static_routes.py +4 -1
- package/latticeai/cli/runtime.py +7 -3
- package/latticeai/core/config.py +47 -3
- package/latticeai/core/csrf.py +28 -2
- package/latticeai/core/embedding_providers/__init__.py +3 -5
- package/latticeai/core/embedding_providers/base.py +1 -1
- package/latticeai/core/embedding_providers/text.py +1 -1
- package/latticeai/core/enterprise.py +1 -4
- package/latticeai/core/http_origin.py +146 -0
- package/latticeai/core/invitations.py +3 -2
- package/latticeai/core/io_utils.py +2 -11
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +5 -0
- package/latticeai/core/model_compat.py +2 -8
- package/latticeai/core/module_probe.py +37 -0
- package/latticeai/core/run_explain.py +5 -2
- package/latticeai/core/run_store.py +2 -2
- package/latticeai/core/security.py +13 -0
- package/latticeai/core/sessions.py +3 -2
- package/latticeai/core/sse.py +25 -0
- package/latticeai/core/users.py +0 -9
- package/latticeai/core/workspace_graph_trace.py +2 -8
- package/latticeai/core/workspace_os.py +0 -10
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/core/workspace_os_utils.py +34 -0
- package/latticeai/core/workspace_review_items.py +2 -8
- package/latticeai/core/workspace_runs.py +2 -8
- package/latticeai/core/workspace_skills.py +2 -7
- package/latticeai/models/router/documents.py +0 -35
- package/latticeai/models/router/registry.py +0 -4
- package/latticeai/runtime/access_runtime.py +16 -4
- package/latticeai/runtime/build_phases/features.py +2 -0
- package/latticeai/runtime/build_phases/web.py +2 -0
- package/latticeai/runtime/feature_toggle_wiring.py +12 -18
- package/latticeai/runtime/network_boundary_wiring.py +8 -19
- package/latticeai/runtime/permission_mode_wiring.py +6 -13
- package/latticeai/runtime/router_registration.py +4 -0
- package/latticeai/runtime/runtime_context.py +1 -13
- package/latticeai/runtime/service_singletons.py +55 -0
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/brain_intelligence/proposals.py +0 -5
- package/latticeai/services/change_proposals.py +2 -36
- package/latticeai/services/chronicle.py +4 -6
- package/latticeai/services/command_center.py +4 -4
- package/latticeai/services/evidence_actions.py +5 -2
- package/latticeai/services/hybrid_chat.py +2 -2
- package/latticeai/services/hybrid_policy.py +8 -57
- package/latticeai/services/mode_store.py +132 -0
- package/latticeai/services/model_capability_registry.py +0 -15
- package/latticeai/services/model_catalog.py +0 -7
- package/latticeai/services/model_loading.py +3 -2
- package/latticeai/services/model_runtime/__init__.py +0 -57
- package/latticeai/services/model_runtime/engines.py +27 -128
- package/latticeai/services/model_runtime/loading.py +2 -2
- package/latticeai/services/network_boundary_service.py +7 -44
- package/latticeai/services/permission_mode_service.py +8 -56
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/setup_detection.py +67 -1
- package/latticeai/services/tool_dispatch.py +0 -4
- package/latticeai/services/upload_service.py +7 -20
- package/latticeai/services/workspace_service.py +0 -6
- package/latticeai/setup/auto_setup.py +15 -26
- package/latticeai/setup/wizard/catalog.py +5 -2
- package/latticeai/setup/wizard/detect.py +6 -27
- package/latticeai/setup/wizard/paths.py +2 -5
- package/latticeai/setup/wizard/plans.py +2 -2
- package/package.json +2 -4
- package/scripts/bump_version.py +14 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_legacy_debt.mjs +1 -1
- package/scripts/generate_agent_loop_fixtures.py +87 -0
- package/scripts/generate_rust_parity_fixtures.py +3 -95
- package/scripts/parity_fixture_corpus_context.py +162 -0
- package/scripts/release_screen_claims.json +10 -0
- package/src-tauri/Cargo.lock +7 -7
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/backend.rs +76 -2
- package/src-tauri/src/main.rs +13 -3
- package/src-tauri/tauri.conf.json +2 -2
- package/static/app/asset-manifest.json +40 -40
- package/static/app/assets/{Act-Drs_jd-O.js → Act-DcQizkl1.js} +1 -1
- package/static/app/assets/{AdminConsole-BqNx5oF6.js → AdminConsole-cf4npybT.js} +1 -1
- package/static/app/assets/{Brain-C7bdNmz-.js → Brain-3VCSHFcn.js} +1 -1
- package/static/app/assets/{BrainHome-DcTnINcI.js → BrainHome-Qm8eaztx.js} +1 -1
- package/static/app/assets/{BrainSignals-Df1MRIPD.js → BrainSignals-DS9BtKOW.js} +1 -1
- package/static/app/assets/{Capture-Dgsxvnbx.js → Capture-DiQ219jW.js} +1 -1
- package/static/app/assets/{Chronicle-BrkTxJK1.js → Chronicle-BGvuAchH.js} +1 -1
- package/static/app/assets/{CommandPalette-DDziw7Oj.js → CommandPalette-Bqhm0Urn.js} +1 -1
- package/static/app/assets/{Library-CYxeuraA.js → Library-BV6NnF0a.js} +1 -1
- package/static/app/assets/{LivingBrain-CNp6mvCm.js → LivingBrain-GzenJchP.js} +1 -1
- package/static/app/assets/{ProductFlow-BVsowu3Z.js → ProductFlow-DEP6-vML.js} +1 -1
- package/static/app/assets/{ReviewCard-D1LbOfJS.js → ReviewCard-CNZ7XjWG.js} +1 -1
- package/static/app/assets/{System-DcRAq7wj.js → System-CieofHQa.js} +1 -1
- package/static/app/assets/arrow-left-kfsrk0mv.js +1 -0
- package/static/app/assets/{bot-BK2xQ9mN.js → bot-B_K1Tdmw.js} +1 -1
- package/static/app/assets/{brain-BZNztFBb.js → brain-DWyaV1L1.js} +1 -1
- package/static/app/assets/{button-CjueubVZ.js → button-aTn4s84A.js} +1 -1
- package/static/app/assets/circle-check-qqLug9nU.js +1 -0
- package/static/app/assets/{circle-pause-HRjSmpyY.js → circle-pause-xKgeGXkT.js} +1 -1
- package/static/app/assets/{circle-play-CMcKDVwu.js → circle-play-DkT6tYPX.js} +1 -1
- package/static/app/assets/{cpu-B_MyTfYr.js → cpu-85xYObUC.js} +1 -1
- package/static/app/assets/{download-CTy0EByV.js → download-B5Fm7YXo.js} +1 -1
- package/static/app/assets/{folder-open-DF6bLhTg.js → folder-open-kk2Xa52u.js} +1 -1
- package/static/app/assets/{hard-drive-CpWHG77C.js → hard-drive-DkA3zBW_.js} +1 -1
- package/static/app/assets/{index-Dd6abJHX.js → index-BMPdTmlY.js} +3 -3
- package/static/app/assets/{input-bVgIRN3s.js → input-B0nRf2jO.js} +1 -1
- package/static/app/assets/{link-2-bg6CG5Vk.js → link-2-Dwb4gnTc.js} +1 -1
- package/static/app/assets/{permissionCopy-DFTf1HDZ.js → permissionCopy-CQDUBrOZ.js} +1 -1
- package/static/app/assets/{primitives-D7D-sQ3T.js → primitives-SNp0LRJz.js} +1 -1
- package/static/app/assets/search-BcHqkjoy.js +1 -0
- package/static/app/assets/{share-2-BLzo7U4L.js → share-2-BsrxFglO.js} +1 -1
- package/static/app/assets/{shield-alert-BmngqnsJ.js → shield-alert-5BStfp2_.js} +1 -1
- package/static/app/assets/{textarea-DOSlAQQ3.js → textarea-Cg8IUA-k.js} +1 -1
- package/static/app/assets/{useFocusTrap-pZhgeee4.js → useFocusTrap-CYKvE46M.js} +1 -1
- package/static/app/assets/{useMutation-BMDwNk4I.js → useMutation-CSn9t1op.js} +1 -1
- package/static/app/assets/{useQuery-CSjKttRo.js → useQuery-CY2OI2uy.js} +1 -1
- package/static/app/assets/{utils-D3u_yv7B.js → utils-Ddol2RWD.js} +1 -1
- package/static/app/assets/{workspace-H3bMjYXC.js → workspace-BqDwOz_p.js} +1 -1
- package/static/app/index.html +3 -3
- package/static/sw.js +1 -1
- package/desktop/electron/README.md +0 -9
- package/desktop/electron/main.cjs +0 -58
- package/desktop/electron/preload.cjs +0 -5
- package/latticeai/core/graph_curator.py +0 -11
- package/latticeai/core/hooks.py +0 -11
- package/latticeai/core/local_embeddings.py +0 -104
- package/latticeai/core/multi_agent.py +0 -11
- package/latticeai/core/workflow_engine.py +0 -11
- package/latticeai/services/ingestion.py +0 -11
- package/latticeai/services/kg_portability.py +0 -11
- package/latticeai/services/multimodal_streaming.py +0 -129
- package/scripts/measure_brain_home_fill.mjs +0 -141
- package/static/app/assets/arrow-left-BHYmOTWU.js +0 -1
- package/static/app/assets/circle-check-CLeWJr67.js +0 -1
- package/static/app/assets/search-BgSVX6NG.js +0 -1
|
@@ -104,81 +104,24 @@ from latticeai.services.model_runtime.engines import (
|
|
|
104
104
|
from latticeai.services.model_runtime.engines import (
|
|
105
105
|
_LOCAL_SERVER_PROCESSES as _LOCAL_SERVER_PROCESSES,
|
|
106
106
|
)
|
|
107
|
-
from latticeai.services.model_runtime.engines import (
|
|
108
|
-
LMSTUDIO_BUNDLED_CLI as LMSTUDIO_BUNDLED_CLI,
|
|
109
|
-
)
|
|
110
107
|
from latticeai.services.model_runtime.engines import (
|
|
111
108
|
LOCAL_SERVER_PROCESSES as LOCAL_SERVER_PROCESSES,
|
|
112
109
|
)
|
|
113
|
-
from latticeai.services.model_runtime.engines import (
|
|
114
|
-
VLLM_METAL_BIN as VLLM_METAL_BIN,
|
|
115
|
-
)
|
|
116
|
-
from latticeai.services.model_runtime.engines import (
|
|
117
|
-
VLLM_METAL_ENV as VLLM_METAL_ENV,
|
|
118
|
-
)
|
|
119
|
-
from latticeai.services.model_runtime.engines import (
|
|
120
|
-
VLLM_METAL_PYTHON as VLLM_METAL_PYTHON,
|
|
121
|
-
)
|
|
122
110
|
from latticeai.services.model_runtime.engines import (
|
|
123
111
|
_engine_install_plan as _engine_install_plan,
|
|
124
112
|
)
|
|
125
|
-
from latticeai.services.model_runtime.engines import (
|
|
126
|
-
_engine_support_status as _engine_support_status,
|
|
127
|
-
)
|
|
128
|
-
from latticeai.services.model_runtime.engines import (
|
|
129
|
-
_ensure_llamacpp_server as _ensure_llamacpp_server,
|
|
130
|
-
)
|
|
131
|
-
from latticeai.services.model_runtime.engines import (
|
|
132
|
-
_ensure_lmstudio_server as _ensure_lmstudio_server,
|
|
133
|
-
)
|
|
134
|
-
from latticeai.services.model_runtime.engines import (
|
|
135
|
-
_ensure_ollama_server as _ensure_ollama_server,
|
|
136
|
-
)
|
|
137
|
-
from latticeai.services.model_runtime.engines import (
|
|
138
|
-
_ensure_vllm_server as _ensure_vllm_server,
|
|
139
|
-
)
|
|
140
|
-
from latticeai.services.model_runtime.engines import (
|
|
141
|
-
_find_lmstudio_cli as _find_lmstudio_cli,
|
|
142
|
-
)
|
|
143
113
|
from latticeai.services.model_runtime.engines import (
|
|
144
114
|
_find_lmstudio_model_key as _find_lmstudio_model_key,
|
|
145
115
|
)
|
|
146
|
-
from latticeai.services.model_runtime.engines import (
|
|
147
|
-
_get_ollama_pulled_models as _get_ollama_pulled_models,
|
|
148
|
-
)
|
|
149
|
-
from latticeai.services.model_runtime.engines import (
|
|
150
|
-
_get_openai_compatible_server_models as _get_openai_compatible_server_models,
|
|
151
|
-
)
|
|
152
116
|
from latticeai.services.model_runtime.engines import (
|
|
153
117
|
_json_request as _json_request,
|
|
154
118
|
)
|
|
155
119
|
from latticeai.services.model_runtime.engines import (
|
|
156
120
|
_lmstudio_candidate_keys as _lmstudio_candidate_keys,
|
|
157
121
|
)
|
|
158
|
-
from latticeai.services.model_runtime.engines import (
|
|
159
|
-
_local_binary as _local_binary,
|
|
160
|
-
)
|
|
161
|
-
from latticeai.services.model_runtime.engines import (
|
|
162
|
-
_pull_ollama_model_with_progress as _pull_ollama_model_with_progress,
|
|
163
|
-
)
|
|
164
122
|
from latticeai.services.model_runtime.engines import (
|
|
165
123
|
_safe_engine_install_plan as _safe_engine_install_plan,
|
|
166
124
|
)
|
|
167
|
-
from latticeai.services.model_runtime.engines import (
|
|
168
|
-
_update_env_file as _update_env_file,
|
|
169
|
-
)
|
|
170
|
-
from latticeai.services.model_runtime.engines import (
|
|
171
|
-
_vllm_executable as _vllm_executable,
|
|
172
|
-
)
|
|
173
|
-
from latticeai.services.model_runtime.engines import (
|
|
174
|
-
_vllm_metal_python as _vllm_metal_python,
|
|
175
|
-
)
|
|
176
|
-
from latticeai.services.model_runtime.engines import (
|
|
177
|
-
_wait_for_openai_compatible_server as _wait_for_openai_compatible_server,
|
|
178
|
-
)
|
|
179
|
-
from latticeai.services.model_runtime.engines import (
|
|
180
|
-
_windows_binary_candidates as _windows_binary_candidates,
|
|
181
|
-
)
|
|
182
125
|
from latticeai.services.model_runtime.engines import (
|
|
183
126
|
engine_installed as engine_installed,
|
|
184
127
|
)
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
"""Local engine discovery, server hand-off, and the LM Studio HTTP client.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Re-exports from ``latticeai.services.model_engines`` that keep the historical
|
|
4
4
|
``model_runtime`` import path alive, plus the one piece of real logic that never
|
|
5
5
|
belonged in the engine layer: the LM Studio native API (list / download / load)
|
|
6
6
|
and its short-lived model cache.
|
|
7
7
|
|
|
8
|
+
Until 11.5.2 the re-exports were fifteen hand-written one-line functions that
|
|
9
|
+
forwarded their arguments, next to second copies of ``_json_request`` and the
|
|
10
|
+
LM Studio base-URL pair. A forwarding wrapper is a place two implementations
|
|
11
|
+
can drift — one of the copies had already grown a different fallback — so the
|
|
12
|
+
names are now bound directly to the engine layer's and there is nothing left to
|
|
13
|
+
disagree with.
|
|
14
|
+
|
|
8
15
|
``_LMSTUDIO_MODELS_CACHE`` and ``_LMSTUDIO_MODELS_CACHE_TS`` are rebindable
|
|
9
16
|
module state and therefore live here and nowhere else — the package
|
|
10
17
|
``__init__`` deliberately does not re-export them, because a
|
|
@@ -15,7 +22,6 @@ the live value.
|
|
|
15
22
|
from __future__ import annotations
|
|
16
23
|
|
|
17
24
|
import importlib.util
|
|
18
|
-
import json
|
|
19
25
|
import os
|
|
20
26
|
import shutil
|
|
21
27
|
import time
|
|
@@ -24,131 +30,55 @@ import urllib.request
|
|
|
24
30
|
from pathlib import Path
|
|
25
31
|
from typing import Any, Dict, List, Optional
|
|
26
32
|
|
|
27
|
-
from latticeai.models.router import
|
|
33
|
+
from latticeai.models.router import AsyncOpenAI
|
|
28
34
|
from latticeai.services.model_engines import (
|
|
29
35
|
LOCAL_SERVER_PROCESSES as _LOCAL_SERVER_PROCESSES,
|
|
30
36
|
)
|
|
31
37
|
from latticeai.services.model_engines import (
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
from latticeai.services.model_engines import (
|
|
35
|
-
engine_support_status as _engine_support_status,
|
|
36
|
-
)
|
|
37
|
-
from latticeai.services.model_engines import (
|
|
38
|
-
ensure_llamacpp_server as _ensure_llamacpp_server,
|
|
38
|
+
_json_request as _json_request,
|
|
39
39
|
)
|
|
40
40
|
from latticeai.services.model_engines import (
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
from latticeai.services.model_engines import (
|
|
44
|
-
ensure_ollama_server as _ensure_ollama_server,
|
|
41
|
+
engine_install_plan as _engine_install_plan,
|
|
45
42
|
)
|
|
46
43
|
from latticeai.services.model_engines import (
|
|
47
|
-
|
|
44
|
+
engine_support_status as engine_support_status,
|
|
48
45
|
)
|
|
49
46
|
from latticeai.services.model_engines import (
|
|
50
|
-
|
|
47
|
+
ensure_llamacpp_server as ensure_llamacpp_server,
|
|
51
48
|
)
|
|
52
49
|
from latticeai.services.model_engines import (
|
|
53
|
-
|
|
50
|
+
ensure_lmstudio_server as ensure_lmstudio_server,
|
|
54
51
|
)
|
|
55
52
|
from latticeai.services.model_engines import (
|
|
56
|
-
|
|
53
|
+
ensure_ollama_server as ensure_ollama_server,
|
|
57
54
|
)
|
|
55
|
+
from latticeai.services.model_engines import ensure_vllm_server as ensure_vllm_server
|
|
56
|
+
from latticeai.services.model_engines import find_lmstudio_cli as find_lmstudio_cli
|
|
58
57
|
from latticeai.services.model_engines import (
|
|
59
|
-
|
|
58
|
+
get_ollama_pulled_models as get_ollama_pulled_models,
|
|
60
59
|
)
|
|
61
60
|
from latticeai.services.model_engines import (
|
|
62
|
-
|
|
61
|
+
get_openai_compatible_server_models as get_openai_compatible_server_models,
|
|
63
62
|
)
|
|
63
|
+
from latticeai.services.model_engines import lmstudio_api_base as lmstudio_api_base
|
|
64
64
|
from latticeai.services.model_engines import (
|
|
65
|
-
|
|
65
|
+
lmstudio_native_api_base as lmstudio_native_api_base,
|
|
66
66
|
)
|
|
67
|
+
from latticeai.services.model_engines import local_binary as local_binary
|
|
67
68
|
from latticeai.services.model_engines import (
|
|
68
|
-
|
|
69
|
+
pull_ollama_model_with_progress as pull_ollama_model_with_progress,
|
|
69
70
|
)
|
|
71
|
+
from latticeai.services.model_engines import vllm_executable as vllm_executable
|
|
72
|
+
from latticeai.services.model_engines import vllm_metal_python as vllm_metal_python
|
|
70
73
|
from latticeai.services.model_engines import (
|
|
71
|
-
wait_for_openai_compatible_server as
|
|
74
|
+
wait_for_openai_compatible_server as wait_for_openai_compatible_server,
|
|
72
75
|
)
|
|
73
76
|
from latticeai.services.model_engines import (
|
|
74
|
-
windows_binary_candidates as
|
|
77
|
+
windows_binary_candidates as windows_binary_candidates,
|
|
75
78
|
)
|
|
76
79
|
from latticeai.services.model_errors import ModelRuntimeError
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
def _update_env_file(env_file: Path, key: str, value: str) -> None:
|
|
80
|
-
lines = []
|
|
81
|
-
found = False
|
|
82
|
-
if env_file.exists():
|
|
83
|
-
for line in env_file.read_text(encoding="utf-8").splitlines():
|
|
84
|
-
if line.startswith(f"{key}="):
|
|
85
|
-
lines.append(f"{key}={value}")
|
|
86
|
-
found = True
|
|
87
|
-
else:
|
|
88
|
-
lines.append(line)
|
|
89
|
-
if not found:
|
|
90
|
-
lines.append(f"{key}={value}")
|
|
91
|
-
env_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
92
|
-
|
|
93
|
-
|
|
94
81
|
LOCAL_SERVER_PROCESSES = _LOCAL_SERVER_PROCESSES
|
|
95
|
-
VLLM_METAL_ENV = Path.home() / ".venv-vllm-metal"
|
|
96
|
-
VLLM_METAL_BIN = VLLM_METAL_ENV / "bin" / "vllm"
|
|
97
|
-
VLLM_METAL_PYTHON = VLLM_METAL_ENV / "bin" / "python"
|
|
98
|
-
LMSTUDIO_BUNDLED_CLI = Path("/Applications/LM Studio.app/Contents/Resources/app/.webpack/lms")
|
|
99
|
-
|
|
100
|
-
def windows_binary_candidates(binary: str) -> List[Path]:
|
|
101
|
-
return _windows_binary_candidates(binary)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def local_binary(binary: str) -> Optional[str]:
|
|
105
|
-
return _local_binary(binary)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def find_lmstudio_cli() -> Optional[str]:
|
|
109
|
-
return _find_lmstudio_cli()
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def vllm_executable() -> Optional[str]:
|
|
113
|
-
return _vllm_executable()
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def vllm_metal_python() -> Optional[str]:
|
|
117
|
-
return _vllm_metal_python()
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def _json_request(
|
|
121
|
-
url: str,
|
|
122
|
-
*,
|
|
123
|
-
method: str = "GET",
|
|
124
|
-
payload: Optional[Dict[str, Any]] = None,
|
|
125
|
-
headers: Optional[Dict[str, str]] = None,
|
|
126
|
-
timeout: float = 10.0,
|
|
127
|
-
) -> Dict[str, Any]:
|
|
128
|
-
data = None
|
|
129
|
-
req_headers = dict(headers or {})
|
|
130
|
-
if payload is not None:
|
|
131
|
-
data = json.dumps(payload).encode("utf-8")
|
|
132
|
-
req_headers.setdefault("Content-Type", "application/json")
|
|
133
|
-
req = urllib.request.Request(url, data=data, headers=req_headers, method=method)
|
|
134
|
-
with urllib.request.urlopen(req, timeout=timeout) as res:
|
|
135
|
-
raw = res.read().decode("utf-8", errors="replace")
|
|
136
|
-
if not raw.strip():
|
|
137
|
-
return {}
|
|
138
|
-
return json.loads(raw)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def lmstudio_api_base() -> str:
|
|
142
|
-
return (os.getenv("LMSTUDIO_BASE_URL") or OPENAI_COMPATIBLE_PROVIDERS["lmstudio"]["base_url"]).rstrip("/")
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def lmstudio_native_api_base() -> str:
|
|
146
|
-
base = lmstudio_api_base()
|
|
147
|
-
return base[:-3] if base.endswith("/v1") else base
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def ensure_lmstudio_server() -> None:
|
|
151
|
-
return _ensure_lmstudio_server()
|
|
152
82
|
|
|
153
83
|
|
|
154
84
|
_LMSTUDIO_MODELS_CACHE: List[Dict[str, Any]] = []
|
|
@@ -280,37 +210,6 @@ def ensure_lmstudio_model(model_name: str) -> Dict[str, Any]:
|
|
|
280
210
|
"cached": False,
|
|
281
211
|
}
|
|
282
212
|
|
|
283
|
-
def engine_support_status(engine: str) -> Dict[str, Any]:
|
|
284
|
-
return _engine_support_status(engine)
|
|
285
|
-
|
|
286
|
-
def pull_ollama_model_with_progress(model_name: str, progress_emit=None) -> Dict[str, Any]:
|
|
287
|
-
return _pull_ollama_model_with_progress(model_name, progress_emit)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
def get_ollama_pulled_models() -> set:
|
|
291
|
-
return _get_ollama_pulled_models()
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
def get_openai_compatible_server_models(provider: str) -> List[str]:
|
|
295
|
-
return _get_openai_compatible_server_models(provider)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
def ensure_ollama_server() -> None:
|
|
299
|
-
return _ensure_ollama_server()
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
def wait_for_openai_compatible_server(provider: str, model_name: Optional[str] = None, timeout: int = 45) -> bool:
|
|
303
|
-
return _wait_for_openai_compatible_server(provider, model_name=model_name, timeout=timeout)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def ensure_vllm_server(model_name: str) -> None:
|
|
307
|
-
return _ensure_vllm_server(model_name)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def ensure_llamacpp_server(model_name: str) -> None:
|
|
311
|
-
return _ensure_llamacpp_server(model_name)
|
|
312
|
-
|
|
313
|
-
|
|
314
213
|
def _safe_engine_install_plan(
|
|
315
214
|
engine: str,
|
|
316
215
|
*,
|
|
@@ -9,10 +9,10 @@ load itself (blocking and streaming forms) and to
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
import json
|
|
13
12
|
from typing import Any, AsyncIterator, Dict, Optional
|
|
14
13
|
|
|
15
14
|
from latticeai.core.model_resolution import ModelResolution as _ModelResolution
|
|
15
|
+
from latticeai.core.sse import sse_frame
|
|
16
16
|
from latticeai.models.router import OPENAI_COMPATIBLE_PROVIDERS, ensure_mlx_runtime
|
|
17
17
|
from latticeai.services.model_catalog import ENGINE_INSTALLERS, MODEL_ENGINE_ALIASES
|
|
18
18
|
from latticeai.services.model_errors import ModelRuntimeError
|
|
@@ -151,7 +151,7 @@ async def prepare_and_load_model(
|
|
|
151
151
|
|
|
152
152
|
|
|
153
153
|
def sse_event(event: str, data: Dict[str, Any]) -> str:
|
|
154
|
-
return
|
|
154
|
+
return sse_frame(event, data)
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
async def prepare_and_load_model_stream(
|
|
@@ -12,12 +12,9 @@ ack is audited. Hard node filters remain in ``latticeai.core.network_boundary``.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
import json
|
|
16
|
-
import threading
|
|
17
15
|
from pathlib import Path
|
|
18
16
|
from typing import Any, Callable, Dict, Optional
|
|
19
17
|
|
|
20
|
-
from latticeai.core.io_utils import atomic_write_json
|
|
21
18
|
from latticeai.core.network_boundary import (
|
|
22
19
|
DEFAULT_NETWORK_MODE,
|
|
23
20
|
NetworkBoundaryMode,
|
|
@@ -25,11 +22,14 @@ from latticeai.core.network_boundary import (
|
|
|
25
22
|
network_mode_contract,
|
|
26
23
|
normalize_network_mode,
|
|
27
24
|
)
|
|
25
|
+
from latticeai.services.mode_store import JsonBackedModeService
|
|
28
26
|
|
|
29
27
|
|
|
30
|
-
class NetworkBoundaryService:
|
|
28
|
+
class NetworkBoundaryService(JsonBackedModeService[NetworkBoundaryMode]):
|
|
31
29
|
"""Load / save the network boundary dial."""
|
|
32
30
|
|
|
31
|
+
FILENAME = "network_boundary.json"
|
|
32
|
+
|
|
33
33
|
def __init__(
|
|
34
34
|
self,
|
|
35
35
|
*,
|
|
@@ -37,36 +37,11 @@ class NetworkBoundaryService:
|
|
|
37
37
|
default_mode: NetworkBoundaryMode | str = DEFAULT_NETWORK_MODE,
|
|
38
38
|
audit: Optional[Callable[..., None]] = None,
|
|
39
39
|
) -> None:
|
|
40
|
-
|
|
40
|
+
super().__init__(data_dir=data_dir, audit=audit)
|
|
41
41
|
self._default = normalize_network_mode(default_mode)
|
|
42
|
-
self._audit = audit or (lambda *a, **kw: None)
|
|
43
|
-
self._lock = threading.Lock()
|
|
44
|
-
|
|
45
|
-
def rebind_data_dir(self, data_dir: Path) -> None:
|
|
46
|
-
with self._lock:
|
|
47
|
-
self._path = Path(data_dir) / "network_boundary.json"
|
|
48
42
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
self._audit = audit
|
|
52
|
-
|
|
53
|
-
def _read(self) -> Dict[str, Any]:
|
|
54
|
-
if not self._path.exists():
|
|
55
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
56
|
-
try:
|
|
57
|
-
data = json.loads(self._path.read_text(encoding="utf-8"))
|
|
58
|
-
except Exception:
|
|
59
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
60
|
-
if not isinstance(data, dict):
|
|
61
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
62
|
-
data.setdefault("default", self._default.value)
|
|
63
|
-
data.setdefault("users", {})
|
|
64
|
-
data.setdefault("workspaces", {})
|
|
65
|
-
return data
|
|
66
|
-
|
|
67
|
-
def _write(self, data: Dict[str, Any]) -> None:
|
|
68
|
-
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
-
atomic_write_json(self._path, data)
|
|
43
|
+
def _default_entry(self) -> Any:
|
|
44
|
+
return self._default.value
|
|
70
45
|
|
|
71
46
|
def _resolve_from(
|
|
72
47
|
self,
|
|
@@ -85,18 +60,6 @@ class NetworkBoundaryService:
|
|
|
85
60
|
return normalize_network_mode(user)
|
|
86
61
|
return normalize_network_mode(data.get("default") or self._default)
|
|
87
62
|
|
|
88
|
-
def resolve(
|
|
89
|
-
self,
|
|
90
|
-
*,
|
|
91
|
-
user_email: Optional[str] = None,
|
|
92
|
-
workspace_id: Optional[str] = None,
|
|
93
|
-
) -> NetworkBoundaryMode:
|
|
94
|
-
with self._lock:
|
|
95
|
-
data = self._read()
|
|
96
|
-
return self._resolve_from(
|
|
97
|
-
data, user_email=user_email, workspace_id=workspace_id,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
63
|
def get(
|
|
101
64
|
self,
|
|
102
65
|
*,
|
|
@@ -14,12 +14,9 @@ not by this store.
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
import json
|
|
18
|
-
import threading
|
|
19
17
|
from pathlib import Path
|
|
20
18
|
from typing import Any, Callable, Dict, Optional
|
|
21
19
|
|
|
22
|
-
from latticeai.core.io_utils import atomic_write_json
|
|
23
20
|
from latticeai.core.permission_mode import (
|
|
24
21
|
DEFAULT_MODE,
|
|
25
22
|
PermissionMode,
|
|
@@ -27,11 +24,14 @@ from latticeai.core.permission_mode import (
|
|
|
27
24
|
mode_contract,
|
|
28
25
|
normalize_mode,
|
|
29
26
|
)
|
|
27
|
+
from latticeai.services.mode_store import JsonBackedModeService
|
|
30
28
|
|
|
31
29
|
|
|
32
|
-
class PermissionModeService:
|
|
30
|
+
class PermissionModeService(JsonBackedModeService[PermissionMode]):
|
|
33
31
|
"""Load / save the autonomy dial."""
|
|
34
32
|
|
|
33
|
+
FILENAME = "permission_mode.json"
|
|
34
|
+
|
|
35
35
|
def __init__(
|
|
36
36
|
self,
|
|
37
37
|
*,
|
|
@@ -39,43 +39,11 @@ class PermissionModeService:
|
|
|
39
39
|
default_mode: PermissionMode | str = DEFAULT_MODE,
|
|
40
40
|
audit: Optional[Callable[..., None]] = None,
|
|
41
41
|
) -> None:
|
|
42
|
-
|
|
42
|
+
super().__init__(data_dir=data_dir, audit=audit)
|
|
43
43
|
self._default = normalize_mode(default_mode)
|
|
44
|
-
self._audit = audit or (lambda *a, **kw: None)
|
|
45
|
-
self._lock = threading.Lock()
|
|
46
|
-
|
|
47
|
-
def rebind_data_dir(self, data_dir: Path) -> None:
|
|
48
|
-
"""Point the store at the app's real data dir.
|
|
49
|
-
|
|
50
|
-
The wiring may instantiate this service lazily before routers know the
|
|
51
|
-
configured data dir; rebinding keeps one file of record instead of
|
|
52
|
-
stranding writes under the fallback path.
|
|
53
|
-
"""
|
|
54
|
-
with self._lock:
|
|
55
|
-
self._path = Path(data_dir) / "permission_mode.json"
|
|
56
44
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
with self._lock:
|
|
60
|
-
self._audit = audit
|
|
61
|
-
|
|
62
|
-
def _read(self) -> Dict[str, Any]:
|
|
63
|
-
if not self._path.exists():
|
|
64
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
65
|
-
try:
|
|
66
|
-
data = json.loads(self._path.read_text(encoding="utf-8"))
|
|
67
|
-
except Exception:
|
|
68
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
69
|
-
if not isinstance(data, dict):
|
|
70
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
71
|
-
data.setdefault("default", self._default.value)
|
|
72
|
-
data.setdefault("users", {})
|
|
73
|
-
data.setdefault("workspaces", {})
|
|
74
|
-
return data
|
|
75
|
-
|
|
76
|
-
def _write(self, data: Dict[str, Any]) -> None:
|
|
77
|
-
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
-
atomic_write_json(self._path, data)
|
|
45
|
+
def _default_entry(self) -> Any:
|
|
46
|
+
return self._default.value
|
|
79
47
|
|
|
80
48
|
def _resolve_from(
|
|
81
49
|
self,
|
|
@@ -84,11 +52,7 @@ class PermissionModeService:
|
|
|
84
52
|
user_email: Optional[str],
|
|
85
53
|
workspace_id: Optional[str],
|
|
86
54
|
) -> PermissionMode:
|
|
87
|
-
"""Scope precedence: workspace → user → process default.
|
|
88
|
-
|
|
89
|
-
Pure over ``data`` and lock-free, so holders of ``_lock`` can reuse it
|
|
90
|
-
without re-entering a non-reentrant lock.
|
|
91
|
-
"""
|
|
55
|
+
"""Scope precedence: workspace → user → process default."""
|
|
92
56
|
if workspace_id:
|
|
93
57
|
ws = (data.get("workspaces") or {}).get(str(workspace_id))
|
|
94
58
|
if ws:
|
|
@@ -99,18 +63,6 @@ class PermissionModeService:
|
|
|
99
63
|
return normalize_mode(user)
|
|
100
64
|
return normalize_mode(data.get("default") or self._default)
|
|
101
65
|
|
|
102
|
-
def resolve(
|
|
103
|
-
self,
|
|
104
|
-
*,
|
|
105
|
-
user_email: Optional[str] = None,
|
|
106
|
-
workspace_id: Optional[str] = None,
|
|
107
|
-
) -> PermissionMode:
|
|
108
|
-
with self._lock:
|
|
109
|
-
data = self._read()
|
|
110
|
-
return self._resolve_from(
|
|
111
|
-
data, user_email=user_email, workspace_id=workspace_id,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
66
|
def get(
|
|
115
67
|
self,
|
|
116
68
|
*,
|
|
@@ -50,6 +50,64 @@ def parse_windows_video_controllers(raw: str) -> List[Dict[str, Any]]:
|
|
|
50
50
|
return controllers
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
|
|
54
|
+
def parse_windows_cpu_info(
|
|
55
|
+
raw: str,
|
|
56
|
+
*,
|
|
57
|
+
model: str,
|
|
58
|
+
physical_cores: int,
|
|
59
|
+
logical_cores: int,
|
|
60
|
+
) -> Tuple[str, int, int]:
|
|
61
|
+
"""Read ``wmic cpu get Name,NumberOfCores,NumberOfLogicalProcessors``.
|
|
62
|
+
|
|
63
|
+
Whatever the output does not say keeps the value the caller already had —
|
|
64
|
+
``wmic`` is absent on modern Windows images and prints nothing there, and
|
|
65
|
+
"0 cores" would be a worse answer than ``os.cpu_count()``'s guess.
|
|
66
|
+
"""
|
|
67
|
+
for line in raw.splitlines():
|
|
68
|
+
key, _, value = line.partition("=")
|
|
69
|
+
if key == "Name" and value.strip():
|
|
70
|
+
model = value.strip()
|
|
71
|
+
elif key == "NumberOfCores" and value.strip():
|
|
72
|
+
try:
|
|
73
|
+
physical_cores = int(value.strip())
|
|
74
|
+
except ValueError:
|
|
75
|
+
quiet()
|
|
76
|
+
elif key == "NumberOfLogicalProcessors" and value.strip():
|
|
77
|
+
try:
|
|
78
|
+
logical_cores = int(value.strip())
|
|
79
|
+
except ValueError:
|
|
80
|
+
quiet()
|
|
81
|
+
return model, physical_cores, logical_cores
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
#: ``IsProcessorFeaturePresent`` codes worth reporting, by instruction name.
|
|
85
|
+
WINDOWS_PROCESSOR_FEATURES: Dict[int, str] = {
|
|
86
|
+
6: "sse", 10: "sse2", 13: "sse3", 19: "neon", 28: "rdrand",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def windows_processor_features() -> List[str]:
|
|
91
|
+
"""Instruction-set flags from the Win32 API, or none anywhere else.
|
|
92
|
+
|
|
93
|
+
``ctypes.windll`` exists only on Windows, so on every other platform this
|
|
94
|
+
raises and answers "nothing known" — which is the honest reading, since the
|
|
95
|
+
flags are unobtainable rather than absent.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
import ctypes
|
|
99
|
+
|
|
100
|
+
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined] # Windows-only
|
|
101
|
+
return [
|
|
102
|
+
name
|
|
103
|
+
for code, name in WINDOWS_PROCESSOR_FEATURES.items()
|
|
104
|
+
if kernel32.IsProcessorFeaturePresent(code)
|
|
105
|
+
]
|
|
106
|
+
except Exception:
|
|
107
|
+
quiet()
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
|
|
53
111
|
def detect_cuda(which: WhichFn, run: RunFn) -> Tuple[bool, str, Optional[str], Optional[str]]:
|
|
54
112
|
nvidia_smi = which("nvidia-smi")
|
|
55
113
|
nvcc = which("nvcc")
|
|
@@ -78,4 +136,12 @@ def detect_tools(which: WhichFn, binaries: Iterable[str]) -> Dict[str, Optional[
|
|
|
78
136
|
return {binary: which(binary) for binary in binaries}
|
|
79
137
|
|
|
80
138
|
|
|
81
|
-
__all__ = [
|
|
139
|
+
__all__ = [
|
|
140
|
+
"WINDOWS_PROCESSOR_FEATURES",
|
|
141
|
+
"detect_cuda",
|
|
142
|
+
"detect_tools",
|
|
143
|
+
"detect_wsl_from_text",
|
|
144
|
+
"parse_windows_cpu_info",
|
|
145
|
+
"parse_windows_video_controllers",
|
|
146
|
+
"windows_processor_features",
|
|
147
|
+
]
|
|
@@ -362,10 +362,6 @@ def configure_tool_dispatch(
|
|
|
362
362
|
)
|
|
363
363
|
|
|
364
364
|
|
|
365
|
-
def agent_policy(action_name: str, args: dict) -> ToolPolicy:
|
|
366
|
-
return DEFAULT_TOOL_DISPATCH_SERVICE.policy_for(action_name, args)
|
|
367
|
-
|
|
368
|
-
|
|
369
365
|
def agent_risk(action_name: str, args: dict) -> str:
|
|
370
366
|
return DEFAULT_TOOL_DISPATCH_SERVICE.risk_level(action_name, args)
|
|
371
367
|
|
|
@@ -6,23 +6,15 @@ import logging
|
|
|
6
6
|
import tempfile
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Optional
|
|
10
9
|
|
|
11
10
|
from fastapi import HTTPException, Request, UploadFile
|
|
12
11
|
|
|
13
12
|
from lattice_brain.ingestion import IngestionItem
|
|
13
|
+
from latticeai.api.workspace_scope import resolve_workspace_scope
|
|
14
14
|
from latticeai.core.quiet import quiet
|
|
15
15
|
from latticeai.tools import ToolError, read_document
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def _workspace_scope_from_request(request: Request) -> Optional[str]:
|
|
19
|
-
header = request.headers.get("X-Workspace-Id")
|
|
20
|
-
if header and header.strip():
|
|
21
|
-
return header.strip()
|
|
22
|
-
query = request.query_params.get("workspace_id")
|
|
23
|
-
return query.strip() if query and query.strip() else None
|
|
24
|
-
|
|
25
|
-
|
|
26
18
|
async def process_uploaded_document(
|
|
27
19
|
*,
|
|
28
20
|
request: Request,
|
|
@@ -39,17 +31,12 @@ async def process_uploaded_document(
|
|
|
39
31
|
workspace_service=None,
|
|
40
32
|
) -> dict:
|
|
41
33
|
enforce_rate_limit(current_user, "upload")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
except PermissionError as exc:
|
|
50
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
51
|
-
else:
|
|
52
|
-
workspace_id = requested_workspace
|
|
34
|
+
workspace_id = resolve_workspace_scope(
|
|
35
|
+
request,
|
|
36
|
+
user=current_user,
|
|
37
|
+
workspace_service=workspace_service,
|
|
38
|
+
write=True,
|
|
39
|
+
)
|
|
53
40
|
suffix = Path(file.filename or "upload").suffix.lower()
|
|
54
41
|
allowed = {".pdf", ".docx", ".xlsx", ".pptx", ".txt", ".md", ".csv"}
|
|
55
42
|
if suffix not in allowed:
|
|
@@ -73,12 +73,6 @@ class WorkspaceService:
|
|
|
73
73
|
self._ensure_permission(workspace_id, user_id, "write")
|
|
74
74
|
return workspace_id
|
|
75
75
|
|
|
76
|
-
def can_read(self, workspace_id: str, user_id: Optional[str]) -> bool:
|
|
77
|
-
return self.store.has_permission(workspace_id, self._identity(user_id), "read")
|
|
78
|
-
|
|
79
|
-
def can_write(self, workspace_id: str, user_id: Optional[str]) -> bool:
|
|
80
|
-
return self.store.has_permission(workspace_id, self._identity(user_id), "write")
|
|
81
|
-
|
|
82
76
|
def readable_workspaces(self, user_id: Optional[str]) -> list[str]:
|
|
83
77
|
"""Return workspace ids the caller can read.
|
|
84
78
|
|