ltcai 11.0.1 → 11.2.0
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 +55 -43
- package/docs/CHANGELOG.md +61 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/FEATURE_AUDIT_v11.2.0.md +393 -0
- package/docs/LAYOUT_REBUILD_SPEC.md +9 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/PERFORMANCE.md +71 -18
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/architecture.md +6 -2
- package/docs/kg-schema.md +1 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +12 -37
- package/lattice_brain/gates.py +125 -0
- package/lattice_brain/graph/discovery_index.py +30 -32
- package/lattice_brain/graph/fusion.py +35 -4
- package/lattice_brain/graph/image_vectors.py +230 -0
- package/lattice_brain/graph/ingest.py +11 -5
- package/lattice_brain/graph/projection.py +66 -8
- package/lattice_brain/graph/provenance.py +27 -2
- package/lattice_brain/graph/retrieval.py +113 -2
- package/lattice_brain/graph/retrieval_docgen.py +6 -6
- package/lattice_brain/graph/schema.py +18 -0
- package/lattice_brain/graph/store.py +9 -0
- package/lattice_brain/graph/vector_index/selector.py +32 -2
- package/lattice_brain/ingestion.py +363 -10
- package/lattice_brain/multimodal.py +1258 -0
- package/lattice_brain/portability.py +169 -32
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/lattice_brain/sealed_box.py +244 -0
- package/lattice_brain/self_model.py +77 -22
- package/lattice_brain/synthesis.py +24 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/brain_intelligence.py +4 -0
- package/latticeai/api/chat.py +11 -0
- package/latticeai/api/chat_helpers.py +16 -3
- package/latticeai/api/chat_hybrid.py +32 -1
- package/latticeai/api/features.py +70 -0
- package/latticeai/api/local_files.py +102 -0
- package/latticeai/api/memory.py +128 -1
- package/latticeai/api/portability.py +39 -4
- package/latticeai/api/review_queue.py +126 -0
- package/latticeai/api/search.py +16 -2
- package/latticeai/core/agent.py +59 -2
- package/latticeai/core/agent_prompts.py +66 -0
- package/latticeai/core/config.py +4 -1
- package/latticeai/core/context_builder.py +98 -11
- package/latticeai/core/embedding_providers.py +528 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +180 -0
- package/latticeai/core/model_compat.py +73 -2
- package/latticeai/core/workspace_os.py +43 -0
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/core/workspace_reorganization.py +335 -0
- package/latticeai/models/model_providers.py +12 -4
- package/latticeai/runtime/build_phases.py +33 -2
- package/latticeai/runtime/chat_wiring.py +4 -0
- package/latticeai/runtime/feature_toggle_wiring.py +163 -0
- package/latticeai/runtime/persistence_runtime.py +41 -4
- package/latticeai/runtime/router_registration.py +11 -0
- package/latticeai/runtime/runtime_context.py +1 -0
- package/latticeai/services/app_context.py +8 -0
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/automation_intelligence.py +22 -2
- package/latticeai/services/brain_intelligence.py +123 -7
- package/latticeai/services/change_proposals.py +50 -10
- package/latticeai/services/command_center.py +10 -4
- package/latticeai/services/feature_toggles.py +502 -0
- package/latticeai/services/folder_watch.py +122 -1
- package/latticeai/services/hybrid_chat.py +56 -5
- package/latticeai/services/interop_bridges.py +978 -0
- package/latticeai/services/memory_service.py +34 -0
- package/latticeai/services/model_capability_registry.py +434 -261
- package/latticeai/services/model_catalog.py +95 -61
- package/latticeai/services/model_recommendation.py +18 -11
- package/latticeai/services/model_runtime.py +1 -1
- package/latticeai/services/multimodal_ports.py +112 -0
- package/latticeai/services/obsidian_bridge.py +16 -25
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/search_service.py +149 -2
- package/latticeai/services/self_model_service.py +171 -0
- package/latticeai/services/tool_dispatch.py +4 -0
- package/latticeai/services/voice_capture.py +27 -1
- package/latticeai/setup/auto_setup.py +27 -30
- package/latticeai/setup/wizard.py +77 -44
- package/package.json +1 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_server_i18n.mjs +1 -0
- package/scripts/release_screen_claims.json +22 -0
- package/scripts/verify_hf_model_registry.py +253 -218
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +37 -37
- package/static/app/assets/{Act-D4zSxFR-.js → Act-AWf0SAKp.js} +1 -1
- package/static/app/assets/{AdminConsole-w5jBfPt2.js → AdminConsole-D0u8Tiyj.js} +1 -1
- package/static/app/assets/{Brain-C2EqQg74.js → Brain-tuhI4sOC.js} +1 -1
- package/static/app/assets/BrainHome-Ts7G_Ila.js +2 -0
- package/static/app/assets/BrainSignals-jMYgQ2Ar.js +1 -0
- package/static/app/assets/{Capture-DPqpGK8d.js → Capture-CqOSzyPr.js} +1 -1
- package/static/app/assets/{CommandPalette-CNf7h5fp.js → CommandPalette-DC0Bzh-I.js} +1 -1
- package/static/app/assets/{Library-BN0HYOfc.js → Library-CX-bbhmK.js} +1 -1
- package/static/app/assets/{LivingBrain-Dfq_wEDI.js → LivingBrain-DBwhto14.js} +1 -1
- package/static/app/assets/{ProductFlow-B-3O0rNV.js → ProductFlow-BHA2cfKI.js} +1 -1
- package/static/app/assets/{ReviewCard-gZ-tdqFM.js → ReviewCard-BUhCKRNM.js} +1 -1
- package/static/app/assets/{System-BElUcSSw.js → System-Bu2t5hn1.js} +1 -1
- package/static/app/assets/arrow-left-Dzwa5zRb.js +1 -0
- package/static/app/assets/{bot--qYHMtkP.js → bot-Cia42c2h.js} +1 -1
- package/static/app/assets/brain-DJMoqrwx.js +1 -0
- package/static/app/assets/{button-51Z3rsuv.js → button-2j2Ijzgq.js} +1 -1
- package/static/app/assets/{circle-pause-CMIiMaQl.js → circle-pause-BEFeWpVW.js} +1 -1
- package/static/app/assets/{circle-play-DZoO_cfG.js → circle-play-ujXMcHxl.js} +1 -1
- package/static/app/assets/{cpu-Bs6uc9W9.js → cpu-k4awryFq.js} +1 -1
- package/static/app/assets/{download-G-2olkWz.js → download-DFbLJ_ig.js} +1 -1
- package/static/app/assets/{folder-open-CTOspnmb.js → folder-open-7y_b6xkM.js} +1 -1
- package/static/app/assets/{hard-drive-CewHWJhn.js → hard-drive-Bidh02Kr.js} +1 -1
- package/static/app/assets/{index-D7Rr-J2Y.js → index-BpYkzcVm.js} +3 -3
- package/static/app/assets/index-DwDl9-8Y.css +2 -0
- package/static/app/assets/{input-D4w_BZWl.js → input-DSlJJxRs.js} +1 -1
- package/static/app/assets/{permissionCopy-CosBEXAZ.js → permissionCopy-Bpb83Hx9.js} +1 -1
- package/static/app/assets/{primitives-d0g9pvzS.js → primitives-BCx6TvfG.js} +1 -1
- package/static/app/assets/search-Cgy8cCFJ.js +1 -0
- package/static/app/assets/{share-2-NmD7e_oV.js → share-2-BH1M-WNi.js} +1 -1
- package/static/app/assets/{shield-alert-CcQeMuju.js → shield-alert-BlKdBXcG.js} +1 -1
- package/static/app/assets/{textarea-BPAJDc-0.js → textarea-CCWbUfFB.js} +1 -1
- package/static/app/assets/{useFocusTrap-C7YLdTBC.js → useFocusTrap-YdHQ7pJ1.js} +1 -1
- package/static/app/assets/{useQuery-DRyD9opW.js → useQuery-CXQiwbVT.js} +1 -1
- package/static/app/assets/{utils-DG1_ExrP.js → utils-zqPZJxdx.js} +2 -2
- package/static/app/assets/{workspace-CWVf3gsI.js → workspace-DXTihhfU.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/BrainHome-CvXS6XiQ.js +0 -2
- package/static/app/assets/BrainSignals-DOE_KhOU.js +0 -1
- package/static/app/assets/arrow-left-CFNIMjhv.js +0 -1
- package/static/app/assets/brain-DDCLjRqO.js +0 -1
- package/static/app/assets/index-CkzokZAj.css +0 -2
- package/static/app/assets/search-BLCYt75v.js +0 -1
|
@@ -210,6 +210,43 @@ MESSAGES: Dict[str, Dict[str, str]] = {
|
|
|
210
210
|
"ko": "'{source}'는 알 수 없는 기억 출처입니다.",
|
|
211
211
|
"en": "Unknown memory source: {source}.",
|
|
212
212
|
},
|
|
213
|
+
# ── self-model (personal ontology) ──────────────────────────────────
|
|
214
|
+
"self_model.invalid_kind": {
|
|
215
|
+
"ko": "나 / 선호 / 습관 / 결정 / 관계 중 하나를 골라주세요.",
|
|
216
|
+
"en": "Choose one of: trait, preference, habit, decision, relationship.",
|
|
217
|
+
},
|
|
218
|
+
"self_model.text_required": {
|
|
219
|
+
"ko": "저장할 내용을 적어주세요.",
|
|
220
|
+
"en": "Please write what should be remembered.",
|
|
221
|
+
},
|
|
222
|
+
"self_model.not_found": {
|
|
223
|
+
"ko": "그 항목은 이미 없습니다.",
|
|
224
|
+
"en": "That entry is already gone.",
|
|
225
|
+
},
|
|
226
|
+
"self_model.not_self_model": {
|
|
227
|
+
"ko": "내 프로필 항목이 아니라서 지울 수 없습니다.",
|
|
228
|
+
"en": "That is not a profile entry, so it cannot be removed here.",
|
|
229
|
+
},
|
|
230
|
+
"self_model.not_a_proposal": {
|
|
231
|
+
"ko": "그 검토 항목은 내 프로필 제안이 아닙니다.",
|
|
232
|
+
"en": "That review item is not a profile proposal.",
|
|
233
|
+
},
|
|
234
|
+
"self_model.empty_proposal": {
|
|
235
|
+
"ko": "그 제안에는 저장할 내용이 없습니다.",
|
|
236
|
+
"en": "That proposal carries nothing to save.",
|
|
237
|
+
},
|
|
238
|
+
"self_model.graph_unavailable": {
|
|
239
|
+
"ko": "지식 그래프가 꺼져 있어 내 프로필을 쓸 수 없습니다.",
|
|
240
|
+
"en": "The knowledge graph is off, so the profile cannot be written.",
|
|
241
|
+
},
|
|
242
|
+
"self_model.queue_unavailable": {
|
|
243
|
+
"ko": "검토함이 연결되어 있지 않아 제안을 처리할 수 없습니다.",
|
|
244
|
+
"en": "The review queue is not connected, so proposals cannot be handled.",
|
|
245
|
+
},
|
|
246
|
+
"self_model.invalid": {
|
|
247
|
+
"ko": "내 프로필을 바꾸지 못했습니다.",
|
|
248
|
+
"en": "The profile could not be changed.",
|
|
249
|
+
},
|
|
213
250
|
"graph.node_id_required": {
|
|
214
251
|
"ko": "노드 식별자가 필요합니다.",
|
|
215
252
|
"en": "A node id is required.",
|
|
@@ -270,6 +307,31 @@ MESSAGES: Dict[str, Dict[str, str]] = {
|
|
|
270
307
|
"ko": "'{status}' 상태의 검토 항목은 승인할 수 없습니다.",
|
|
271
308
|
"en": "A review item in status '{status}' cannot be approved.",
|
|
272
309
|
},
|
|
310
|
+
"review.bulk_ids_required": {
|
|
311
|
+
"ko": "한꺼번에 처리할 검토 항목을 하나 이상 골라 주세요.",
|
|
312
|
+
"en": "Choose at least one review item to act on.",
|
|
313
|
+
},
|
|
314
|
+
"review.bulk_too_many": {
|
|
315
|
+
"ko": "한 번에 최대 {cap}개까지 처리할 수 있습니다.",
|
|
316
|
+
"en": "At most {cap} items can be handled in one request.",
|
|
317
|
+
},
|
|
318
|
+
# ── interop bridges (Notion export / git / mail / calendar) ──────────
|
|
319
|
+
"ingestion.interop_path_required": {
|
|
320
|
+
"ko": "불러올 파일이나 폴더 경로가 필요합니다.",
|
|
321
|
+
"en": "A file or folder path to read is required.",
|
|
322
|
+
},
|
|
323
|
+
"ingestion.interop_unknown_source": {
|
|
324
|
+
"ko": "알 수 없는 연동 종류입니다: {source}",
|
|
325
|
+
"en": "Unknown interop source: {source}",
|
|
326
|
+
},
|
|
327
|
+
"ingestion.vault_watch_unavailable": {
|
|
328
|
+
"ko": "보관함 자동 확인 기능을 사용할 수 없습니다.",
|
|
329
|
+
"en": "Vault watch is unavailable.",
|
|
330
|
+
},
|
|
331
|
+
"ingestion.vault_watch_disabled": {
|
|
332
|
+
"ko": "보관함 자동 확인은 기본으로 꺼져 있습니다. 설정에서 켜야 사용할 수 있습니다.",
|
|
333
|
+
"en": "Vault watch is off by default; turn it on in settings to use it.",
|
|
334
|
+
},
|
|
273
335
|
# ── projects ────────────────────────────────────────────────────────
|
|
274
336
|
"project.not_found": {
|
|
275
337
|
"ko": "프로젝트를 찾을 수 없습니다.",
|
|
@@ -280,6 +342,124 @@ MESSAGES: Dict[str, Dict[str, str]] = {
|
|
|
280
342
|
"ko": "혼합 검색 정책 서비스가 설정되지 않았습니다.",
|
|
281
343
|
"en": "The hybrid policy service is not configured.",
|
|
282
344
|
},
|
|
345
|
+
# ── feature toggles ─────────────────────────────────────────────────
|
|
346
|
+
# The switchboard renders from the server (see
|
|
347
|
+
# ``latticeai/services/feature_toggles.py``), so every label and one-line
|
|
348
|
+
# explanation a person reads lives here. The rule for the summaries: say
|
|
349
|
+
# what turning it *on* does, in the words someone who never read the
|
|
350
|
+
# environment-variable docs would use.
|
|
351
|
+
"features.note": {
|
|
352
|
+
"ko": "모두 지금 바로 적용됩니다. 다시 시작하지 않아도 됩니다.",
|
|
353
|
+
"en": "Every switch here takes effect right away — no restart needed.",
|
|
354
|
+
},
|
|
355
|
+
"features.unknown": {
|
|
356
|
+
"ko": "그런 기능은 없습니다: {feature}",
|
|
357
|
+
"en": "There is no such feature: {feature}",
|
|
358
|
+
},
|
|
359
|
+
"features.invalid_value": {
|
|
360
|
+
"ko": "이 기능에 쓸 수 없는 값입니다: {value}",
|
|
361
|
+
"en": "That is not a value this feature can take: {value}",
|
|
362
|
+
},
|
|
363
|
+
"features.choice.install_required": {
|
|
364
|
+
"ko": "설치 필요 — {reason}",
|
|
365
|
+
"en": "Install required — {reason}",
|
|
366
|
+
},
|
|
367
|
+
"features.allow_multimodal.label": {
|
|
368
|
+
"ko": "사진·녹음도 기억하기",
|
|
369
|
+
"en": "Remember pictures and recordings",
|
|
370
|
+
},
|
|
371
|
+
"features.allow_multimodal.summary": {
|
|
372
|
+
"ko": "폴더를 읽을 때 글뿐 아니라 사진과 녹음도 함께 저장합니다.",
|
|
373
|
+
"en": "A folder scan stores pictures and recordings too, not just text.",
|
|
374
|
+
},
|
|
375
|
+
"features.video_ingest.label": {
|
|
376
|
+
"ko": "영상도 함께",
|
|
377
|
+
"en": "Include videos",
|
|
378
|
+
},
|
|
379
|
+
"features.video_ingest.summary": {
|
|
380
|
+
"ko": "사진·녹음을 켠 상태에서, 영상은 장면과 자막으로 저장합니다.",
|
|
381
|
+
"en": "With the switch above on, videos are stored as keyframes and subtitles.",
|
|
382
|
+
},
|
|
383
|
+
"features.vault_watch.label": {
|
|
384
|
+
"ko": "노트 보관함 지켜보기",
|
|
385
|
+
"en": "Watch my notes vault",
|
|
386
|
+
},
|
|
387
|
+
"features.vault_watch.summary": {
|
|
388
|
+
"ko": "밖에 있는 노트 보관함이 바뀌면 알아서 다시 읽어옵니다.",
|
|
389
|
+
"en": "When an outside notes vault changes, it is re-read on its own.",
|
|
390
|
+
},
|
|
391
|
+
"features.brain_network.label": {
|
|
392
|
+
"ko": "골라서 나누기",
|
|
393
|
+
"en": "Share selected knowledge",
|
|
394
|
+
},
|
|
395
|
+
"features.brain_network.summary": {
|
|
396
|
+
"ko": "내가 고른 기억 묶음만 다른 기기로 내보내고 받아올 수 있습니다.",
|
|
397
|
+
"en": "Lets you export a hand-picked slice of memory to another device, and receive one.",
|
|
398
|
+
},
|
|
399
|
+
"features.brain_network.caution": {
|
|
400
|
+
"ko": "이 기능만 기억을 이 컴퓨터 밖으로 내보냅니다. 받은 내용은 바로 합쳐지지 않고 검토함으로 갑니다.",
|
|
401
|
+
"en": "This is the one switch that sends memory off this computer. Anything received waits in the review inbox instead of merging.",
|
|
402
|
+
},
|
|
403
|
+
"features.synthesis.label": {
|
|
404
|
+
"ko": "스스로 정리하기",
|
|
405
|
+
"en": "Tidy up on its own",
|
|
406
|
+
},
|
|
407
|
+
"features.synthesis.summary": {
|
|
408
|
+
"ko": "자료가 쌓이면 알아서 훑어보고, 고칠 거리를 검토함에 제안합니다.",
|
|
409
|
+
"en": "As material piles up, the Brain reviews it and proposes tidy-ups in the review inbox.",
|
|
410
|
+
},
|
|
411
|
+
"features.auto_vector_index.label": {
|
|
412
|
+
"ko": "넣자마자 검색 준비",
|
|
413
|
+
"en": "Make new material searchable at once",
|
|
414
|
+
},
|
|
415
|
+
"features.auto_vector_index.summary": {
|
|
416
|
+
"ko": "새 자료를 넣으면 바로 의미 검색까지 준비합니다. 끄면 나중에 한 번에 만듭니다.",
|
|
417
|
+
"en": "New material is prepared for meaning-based search immediately; off means you rebuild later.",
|
|
418
|
+
},
|
|
419
|
+
"features.auto_late_fusion.label": {
|
|
420
|
+
"ko": "글로 사진 찾기",
|
|
421
|
+
"en": "Find pictures by typing",
|
|
422
|
+
},
|
|
423
|
+
"features.auto_late_fusion.summary": {
|
|
424
|
+
"ko": "글로 물어봐도 사진까지 함께 찾습니다. 사진을 읽는 모델이 있어야 켜집니다.",
|
|
425
|
+
"en": "A typed question also searches pictures — needs a vision model that shares the same space.",
|
|
426
|
+
},
|
|
427
|
+
"features.fusion_rrf.label": {
|
|
428
|
+
"ko": "검색 결과 합치는 방식 바꾸기",
|
|
429
|
+
"en": "Blend search results by rank",
|
|
430
|
+
},
|
|
431
|
+
"features.fusion_rrf.summary": {
|
|
432
|
+
"ko": "점수 대신 순위로 합칩니다. 검색 채널마다 점수 크기가 달라도 흔들리지 않습니다.",
|
|
433
|
+
"en": "Combines channels by position instead of score, so mismatched score scales stop skewing results.",
|
|
434
|
+
},
|
|
435
|
+
"features.graph_expansion.label": {
|
|
436
|
+
"ko": "옆에 있는 기억까지 보기",
|
|
437
|
+
"en": "Look at neighbouring memories",
|
|
438
|
+
},
|
|
439
|
+
"features.graph_expansion.summary": {
|
|
440
|
+
"ko": "찾은 기억과 바로 이어진 기억도 후보로 넣습니다. 답은 넓어지고 조금 흐려집니다.",
|
|
441
|
+
"en": "Adds memories one link away from a hit as candidates — wider answers, slightly less focused.",
|
|
442
|
+
},
|
|
443
|
+
"features.vector_backend.label": {
|
|
444
|
+
"ko": "의미 검색 방식",
|
|
445
|
+
"en": "Meaning-search engine",
|
|
446
|
+
},
|
|
447
|
+
"features.vector_backend.summary": {
|
|
448
|
+
"ko": "빠르기와 정확함 사이에서 고릅니다. 기본값은 전부 훑어보는 정확한 방식입니다.",
|
|
449
|
+
"en": "Trade speed against exactness. The default compares everything and is exact.",
|
|
450
|
+
},
|
|
451
|
+
"features.vector_backend.choice.brute": {
|
|
452
|
+
"ko": "전부 비교 (정확)",
|
|
453
|
+
"en": "Compare everything (exact)",
|
|
454
|
+
},
|
|
455
|
+
"features.vector_backend.choice.quantized": {
|
|
456
|
+
"ko": "간추려 비교 (빠름)",
|
|
457
|
+
"en": "Compare compressed (faster)",
|
|
458
|
+
},
|
|
459
|
+
"features.vector_backend.choice.hnsw": {
|
|
460
|
+
"ko": "근사 검색 (가장 빠름)",
|
|
461
|
+
"en": "Approximate search (fastest)",
|
|
462
|
+
},
|
|
283
463
|
# ── models ──────────────────────────────────────────────────────────
|
|
284
464
|
"models.other_user_credentials": {
|
|
285
465
|
"ko": "다른 사용자의 모델 자격 증명을 사용할 수 없습니다.",
|
|
@@ -32,14 +32,54 @@ FAMILY_PATTERNS: List[Tuple[str, re.Pattern]] = [
|
|
|
32
32
|
("qwen", re.compile(r"qwen", re.I)),
|
|
33
33
|
("llama", re.compile(r"\bllama|meta[-_]?llama", re.I)),
|
|
34
34
|
("claude", re.compile(r"claude", re.I)),
|
|
35
|
+
# gpt-oss is its own local family with its own chat format, so it must be
|
|
36
|
+
# matched before the cloud `gpt` pattern rather than falling into it.
|
|
37
|
+
("gpt_oss", re.compile(r"gpt[-_]?oss", re.I)),
|
|
35
38
|
("gpt", re.compile(r"gpt[-_]?(?:4|5)|openai", re.I)),
|
|
36
39
|
("gemini", re.compile(r"gemini", re.I)),
|
|
37
40
|
("grok", re.compile(r"grok|x[-_]?ai", re.I)),
|
|
41
|
+
("lfm2", re.compile(r"\blfm[-_]?2", re.I)),
|
|
38
42
|
]
|
|
39
43
|
|
|
44
|
+
#: HF ``config.json`` ``model_type`` → family code. An id is a name somebody
|
|
45
|
+
#: chose; an architecture is what mlx-lm / mlx-vlm actually dispatches on, so it
|
|
46
|
+
#: is the more reliable signal when we have it. Values here are the architectures
|
|
47
|
+
#: pinned in the model capability registry (11.2.0) plus the ones that came
|
|
48
|
+
#: before, so a model already on disk keeps its profile after its generation
|
|
49
|
+
#: stops being offered.
|
|
50
|
+
ARCHITECTURE_FAMILIES: Dict[str, str] = {
|
|
51
|
+
"gemma2": "gemma",
|
|
52
|
+
"gemma3": "gemma",
|
|
53
|
+
"gemma4": "gemma",
|
|
54
|
+
"gemma4_unified": "gemma",
|
|
55
|
+
"qwen2_5_vl": "qwen",
|
|
56
|
+
"qwen3_vl": "qwen",
|
|
57
|
+
"qwen3_vl_moe": "qwen",
|
|
58
|
+
"qwen3_5": "qwen",
|
|
59
|
+
"qwen3_5_moe": "qwen",
|
|
60
|
+
"llama": "llama",
|
|
61
|
+
"llama4": "llama",
|
|
62
|
+
"mllama": "llama",
|
|
63
|
+
"gpt_oss": "gpt_oss",
|
|
64
|
+
"lfm2": "lfm2",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def family_for_architecture(architecture: Optional[str]) -> str:
|
|
69
|
+
"""Map an HF ``model_type`` to a family code (``"unknown"`` when unmapped)."""
|
|
70
|
+
return ARCHITECTURE_FAMILIES.get(str(architecture or "").strip().lower(), "unknown")
|
|
71
|
+
|
|
40
72
|
|
|
41
|
-
def detect_model_family(model_id: str) -> str:
|
|
42
|
-
"""주어진 model_id 문자열에서 family 코드를 추론한다.
|
|
73
|
+
def detect_model_family(model_id: str, architecture: Optional[str] = None) -> str:
|
|
74
|
+
"""주어진 model_id 문자열에서 family 코드를 추론한다.
|
|
75
|
+
|
|
76
|
+
``architecture`` (HF config ``model_type``) 가 주어지고 알려진 값이면 그것을
|
|
77
|
+
우선한다 — 로더가 실제로 분기하는 값이기 때문이다.
|
|
78
|
+
"""
|
|
79
|
+
if architecture:
|
|
80
|
+
by_arch = family_for_architecture(architecture)
|
|
81
|
+
if by_arch != "unknown":
|
|
82
|
+
return by_arch
|
|
43
83
|
if not model_id:
|
|
44
84
|
return "unknown"
|
|
45
85
|
raw = str(model_id)
|
|
@@ -96,6 +136,35 @@ FAMILY_PROFILES: Dict[str, Dict[str, Any]] = {
|
|
|
96
136
|
"disable_draft": False,
|
|
97
137
|
"postprocess": ["strip_role_tokens"],
|
|
98
138
|
},
|
|
139
|
+
# gpt-oss speaks the "harmony" response format: the channel markers are
|
|
140
|
+
# stop sequences, not text, so the reply never has to be salvaged from them.
|
|
141
|
+
"gpt_oss": {
|
|
142
|
+
"family": "gpt_oss",
|
|
143
|
+
"supports_system": True,
|
|
144
|
+
"supports_vision": False,
|
|
145
|
+
"chat_template": "tokenizer_default",
|
|
146
|
+
"preferred_engines": ["local_mlx", "ollama", "llamacpp"],
|
|
147
|
+
"temperature": 0.2,
|
|
148
|
+
"top_p": 0.9,
|
|
149
|
+
"max_tokens": 4096,
|
|
150
|
+
"stop_sequences": ["<|return|>", "<|call|>", "<|endoftext|>"],
|
|
151
|
+
"disable_draft": False,
|
|
152
|
+
"postprocess": ["strip_role_tokens"],
|
|
153
|
+
},
|
|
154
|
+
# LFM2 uses a ChatML-style template; text only, so no vision claim.
|
|
155
|
+
"lfm2": {
|
|
156
|
+
"family": "lfm2",
|
|
157
|
+
"supports_system": True,
|
|
158
|
+
"supports_vision": False,
|
|
159
|
+
"chat_template": "tokenizer_default",
|
|
160
|
+
"preferred_engines": ["local_mlx", "ollama", "llamacpp"],
|
|
161
|
+
"temperature": 0.2,
|
|
162
|
+
"top_p": 0.9,
|
|
163
|
+
"max_tokens": 4096,
|
|
164
|
+
"stop_sequences": ["<|im_end|>", "<|endoftext|>"],
|
|
165
|
+
"disable_draft": False,
|
|
166
|
+
"postprocess": ["strip_role_tokens"],
|
|
167
|
+
},
|
|
99
168
|
"unknown": {
|
|
100
169
|
"family": "unknown",
|
|
101
170
|
"supports_system": True,
|
|
@@ -623,9 +692,11 @@ def get_stop_sequences(model_id: str, engine: Optional[str] = None) -> List[str]
|
|
|
623
692
|
|
|
624
693
|
|
|
625
694
|
__all__ = [
|
|
695
|
+
"ARCHITECTURE_FAMILIES",
|
|
626
696
|
"FAMILY_PROFILES",
|
|
627
697
|
"CompatProfile",
|
|
628
698
|
"detect_model_family",
|
|
699
|
+
"family_for_architecture",
|
|
629
700
|
"friendly_model_runtime_error",
|
|
630
701
|
"get_model_profile",
|
|
631
702
|
"model_runtime_compatibility",
|
|
@@ -53,6 +53,7 @@ from .workspace_permissions import ( # type: ignore
|
|
|
53
53
|
from .workspace_plugins import WorkspacePluginManager
|
|
54
54
|
from .workspace_relationships import WorkspaceRelationships
|
|
55
55
|
from .workspace_relationships import shortest_path as _shortest_path
|
|
56
|
+
from .workspace_reorganization import plan_reorganization
|
|
56
57
|
from .workspace_review_items import WorkspaceReviewItems
|
|
57
58
|
from .workspace_runs import WorkspaceRuns
|
|
58
59
|
from .workspace_skills import WorkspaceSkills
|
|
@@ -839,6 +840,48 @@ class WorkspaceOSStore:
|
|
|
839
840
|
def update_review_item(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
840
841
|
return self.review_items.update_review_item(*args, **kwargs)
|
|
841
842
|
|
|
843
|
+
# ------------------------------------------------------------------
|
|
844
|
+
# Agent-native workspace: folder reorganization (v11.1.0)
|
|
845
|
+
# ------------------------------------------------------------------
|
|
846
|
+
|
|
847
|
+
def preview_reorganization(
|
|
848
|
+
self, *, root: str = "", resolve_path: Callable[[str], Any], graph: Any = None,
|
|
849
|
+
max_moves: int = 20,
|
|
850
|
+
) -> Dict[str, Any]:
|
|
851
|
+
"""What a reorganization would do — read-only, nothing is staged."""
|
|
852
|
+
return plan_reorganization(
|
|
853
|
+
root=root, resolve_path=resolve_path, graph=graph, max_moves=max_moves
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
def propose_reorganization(
|
|
857
|
+
self, *, root: str = "", change_proposals: Any, graph: Any = None,
|
|
858
|
+
user_email: Optional[str] = None, workspace_id: Optional[str] = None,
|
|
859
|
+
max_moves: int = 20,
|
|
860
|
+
) -> Dict[str, Any]:
|
|
861
|
+
"""Stage a folder reorganization for review and record it on the timeline.
|
|
862
|
+
|
|
863
|
+
The staging itself belongs to ``ChangeProposalService`` (one apply path
|
|
864
|
+
for every staged change); this seam exists so a long-running agent
|
|
865
|
+
session asking the Workspace OS to tidy a project leaves a trace of
|
|
866
|
+
having asked, whether or not there was anything to propose.
|
|
867
|
+
"""
|
|
868
|
+
result = change_proposals.propose_reorganization(
|
|
869
|
+
root=root, graph=graph, user_email=user_email,
|
|
870
|
+
workspace_id=workspace_id, max_moves=max_moves,
|
|
871
|
+
)
|
|
872
|
+
proposed = result.get("proposed") or {}
|
|
873
|
+
self.record_timeline_event(
|
|
874
|
+
"workspace",
|
|
875
|
+
"workspace_reorg_proposed",
|
|
876
|
+
{
|
|
877
|
+
"root": root,
|
|
878
|
+
"moves": (result.get("plan") or {}).get("move_count", 0),
|
|
879
|
+
"proposal_id": proposed.get("id"),
|
|
880
|
+
},
|
|
881
|
+
workspace_id=workspace_id,
|
|
882
|
+
)
|
|
883
|
+
return result
|
|
884
|
+
|
|
842
885
|
# ------------------------------------------------------------------
|
|
843
886
|
# Relationship explorer
|
|
844
887
|
# ------------------------------------------------------------------
|
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
from typing import Dict
|
|
12
12
|
|
|
13
|
-
WORKSPACE_OS_VERSION = "11.0
|
|
13
|
+
WORKSPACE_OS_VERSION = "11.2.0"
|
|
14
14
|
|
|
15
15
|
# Workspace types separate single-user Personal workspaces from shared
|
|
16
16
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|