ltcai 4.0.1 → 4.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 +33 -24
- package/desktop/electron/main.cjs +44 -0
- package/docs/CHANGELOG.md +84 -0
- package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +65 -0
- package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +70 -0
- package/docs/V4_1_VALIDATION_REPORT.md +47 -0
- package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +97 -0
- package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +91 -0
- package/docs/V4_2_VALIDATION_REPORT.md +89 -0
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +31 -26
- package/frontend/index.html +24 -0
- package/frontend/openapi.json +14436 -0
- package/frontend/src/App.tsx +184 -0
- package/frontend/src/api/client.ts +320 -0
- package/frontend/src/api/openapi.ts +16921 -0
- package/frontend/src/components/primitives.tsx +204 -0
- package/frontend/src/components/ui/badge.tsx +27 -0
- package/frontend/src/components/ui/button.tsx +37 -0
- package/frontend/src/components/ui/card.tsx +22 -0
- package/frontend/src/components/ui/input.tsx +16 -0
- package/frontend/src/components/ui/textarea.tsx +16 -0
- package/frontend/src/lib/utils.ts +33 -0
- package/frontend/src/main.tsx +23 -0
- package/frontend/src/pages/Act.tsx +245 -0
- package/frontend/src/pages/Ask.tsx +200 -0
- package/frontend/src/pages/Brain.tsx +267 -0
- package/frontend/src/pages/Capture.tsx +158 -0
- package/frontend/src/pages/Library.tsx +187 -0
- package/frontend/src/pages/System.tsx +378 -0
- package/frontend/src/routes.ts +85 -0
- package/frontend/src/store/appStore.ts +54 -0
- package/frontend/src/styles.css +107 -0
- package/kg_schema.py +1 -1
- package/knowledge_graph.py +4 -4
- package/lattice_brain/__init__.py +70 -0
- package/lattice_brain/_kg_common.py +1 -0
- package/lattice_brain/archive.py +133 -0
- package/lattice_brain/context.py +3 -0
- package/lattice_brain/conversations.py +3 -0
- package/lattice_brain/core.py +82 -0
- package/lattice_brain/discovery.py +1 -0
- package/lattice_brain/documents.py +1 -0
- package/lattice_brain/embeddings.py +82 -0
- package/lattice_brain/identity.py +13 -0
- package/lattice_brain/ingest.py +1 -0
- package/lattice_brain/memory.py +3 -0
- package/lattice_brain/network.py +1 -0
- package/lattice_brain/projection.py +1 -0
- package/lattice_brain/provenance.py +1 -0
- package/lattice_brain/retrieval.py +1 -0
- package/lattice_brain/schema.py +1 -0
- package/lattice_brain/storage/__init__.py +22 -0
- package/lattice_brain/storage/base.py +72 -0
- package/lattice_brain/storage/docker.py +105 -0
- package/lattice_brain/storage/factory.py +31 -0
- package/lattice_brain/storage/migration.py +190 -0
- package/lattice_brain/storage/postgres.py +123 -0
- package/lattice_brain/storage/sqlite.py +128 -0
- package/lattice_brain/store.py +3 -0
- package/lattice_brain/write_master.py +1 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/portability.py +69 -0
- package/latticeai/api/setup.py +5 -4
- package/latticeai/api/static_routes.py +4 -4
- package/latticeai/app_factory.py +17 -10
- package/latticeai/brain/__init__.py +6 -6
- package/latticeai/brain/_kg_common.py +1 -1
- package/latticeai/brain/network.py +1 -1
- package/latticeai/brain/retrieval.py +15 -0
- package/latticeai/brain/store.py +22 -6
- package/latticeai/core/config.py +8 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/kg_portability.py +82 -1
- package/package.json +55 -15
- package/scripts/build_frontend_assets.mjs +38 -0
- package/scripts/bump_version.py +4 -1
- package/scripts/export_openapi.py +31 -0
- package/scripts/lint_frontend.mjs +91 -0
- package/scripts/migrate_brain_storage.py +53 -0
- package/scripts/run_python.mjs +47 -0
- package/scripts/wheel_smoke.py +3 -0
- package/src-tauri/Cargo.lock +4833 -0
- package/src-tauri/Cargo.toml +19 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/capabilities/default.json +7 -0
- package/src-tauri/src/main.rs +78 -0
- package/src-tauri/tauri.conf.json +39 -0
- package/static/app/asset-manifest.json +32 -0
- package/static/app/assets/core-CwxXejkd.js +2 -0
- package/static/app/assets/core-CwxXejkd.js.map +1 -0
- package/static/app/assets/index-CDjiH_se.css +2 -0
- package/static/app/assets/index-C_HAkbAg.js +333 -0
- package/static/app/assets/index-C_HAkbAg.js.map +1 -0
- package/static/app/index.html +25 -0
- package/static/manifest.json +2 -2
- package/static/sw.js +4 -4
- package/scripts/build_v3_assets.mjs +0 -170
- package/scripts/lint_v3.mjs +0 -120
- package/static/v3/asset-manifest.json +0 -63
- package/static/v3/css/lattice.base.49deefb5.css +0 -128
- package/static/v3/css/lattice.base.css +0 -128
- package/static/v3/css/lattice.components.cde18231.css +0 -472
- package/static/v3/css/lattice.components.css +0 -472
- package/static/v3/css/lattice.shell.29d36d85.css +0 -452
- package/static/v3/css/lattice.shell.css +0 -452
- package/static/v3/css/lattice.tokens.304cbc40.css +0 -135
- package/static/v3/css/lattice.tokens.css +0 -135
- package/static/v3/css/lattice.views.0a18b6c5.css +0 -360
- package/static/v3/css/lattice.views.css +0 -360
- package/static/v3/index.html +0 -68
- package/static/v3/js/app.c5c80c46.js +0 -26
- package/static/v3/js/app.js +0 -26
- package/static/v3/js/core/api.ba0fbf14.js +0 -625
- package/static/v3/js/core/api.js +0 -625
- package/static/v3/js/core/components.f25b3b93.js +0 -230
- package/static/v3/js/core/components.js +0 -230
- package/static/v3/js/core/dom.a2773eb0.js +0 -148
- package/static/v3/js/core/dom.js +0 -148
- package/static/v3/js/core/i18n.880e1fec.js +0 -575
- package/static/v3/js/core/i18n.js +0 -575
- package/static/v3/js/core/router.584570f2.js +0 -37
- package/static/v3/js/core/router.js +0 -37
- package/static/v3/js/core/routes.37522821.js +0 -101
- package/static/v3/js/core/routes.js +0 -101
- package/static/v3/js/core/shell.e3f6bbfa.js +0 -420
- package/static/v3/js/core/shell.js +0 -420
- package/static/v3/js/core/store.7b2aa044.js +0 -123
- package/static/v3/js/core/store.js +0 -123
- package/static/v3/js/views/account.eff40715.js +0 -143
- package/static/v3/js/views/account.js +0 -143
- package/static/v3/js/views/activity.0d271ef9.js +0 -67
- package/static/v3/js/views/activity.js +0 -67
- package/static/v3/js/views/admin-audit.660a1fb1.js +0 -185
- package/static/v3/js/views/admin-audit.js +0 -185
- package/static/v3/js/views/admin-permissions.a7ae5f09.js +0 -177
- package/static/v3/js/views/admin-permissions.js +0 -177
- package/static/v3/js/views/admin-policies.3658fd86.js +0 -102
- package/static/v3/js/views/admin-policies.js +0 -102
- package/static/v3/js/views/admin-private-vpc.7d342d36.js +0 -135
- package/static/v3/js/views/admin-private-vpc.js +0 -135
- package/static/v3/js/views/admin-security.07c66b72.js +0 -180
- package/static/v3/js/views/admin-security.js +0 -180
- package/static/v3/js/views/admin-users.f7ac7b43.js +0 -166
- package/static/v3/js/views/admin-users.js +0 -166
- package/static/v3/js/views/agents.17c5288d.js +0 -564
- package/static/v3/js/views/agents.js +0 -564
- package/static/v3/js/views/chat.e250e2cc.js +0 -624
- package/static/v3/js/views/chat.js +0 -624
- package/static/v3/js/views/files.adad14c1.js +0 -365
- package/static/v3/js/views/files.js +0 -365
- package/static/v3/js/views/graph-canvas.17c15d65.js +0 -509
- package/static/v3/js/views/graph-canvas.js +0 -509
- package/static/v3/js/views/home.24f8b8ae.js +0 -200
- package/static/v3/js/views/home.js +0 -200
- package/static/v3/js/views/hooks.37895880.js +0 -220
- package/static/v3/js/views/hooks.js +0 -220
- package/static/v3/js/views/hybrid-search.2fb63ed9.js +0 -194
- package/static/v3/js/views/hybrid-search.js +0 -194
- package/static/v3/js/views/knowledge-graph.4d09c537.js +0 -529
- package/static/v3/js/views/knowledge-graph.js +0 -529
- package/static/v3/js/views/marketplace.ab0583d4.js +0 -141
- package/static/v3/js/views/marketplace.js +0 -141
- package/static/v3/js/views/mcp.99b5c6a7.js +0 -114
- package/static/v3/js/views/mcp.js +0 -114
- package/static/v3/js/views/memory.4ebdf474.js +0 -147
- package/static/v3/js/views/memory.js +0 -147
- package/static/v3/js/views/models.a1ffa147.js +0 -256
- package/static/v3/js/views/models.js +0 -256
- package/static/v3/js/views/my-computer.d9d9ae1c.js +0 -463
- package/static/v3/js/views/my-computer.js +0 -463
- package/static/v3/js/views/network.52a4f181.js +0 -97
- package/static/v3/js/views/network.js +0 -97
- package/static/v3/js/views/pipeline.c522f1ce.js +0 -157
- package/static/v3/js/views/pipeline.js +0 -157
- package/static/v3/js/views/planning.4876fd77.js +0 -174
- package/static/v3/js/views/planning.js +0 -174
- package/static/v3/js/views/runs.b63b2afa.js +0 -144
- package/static/v3/js/views/runs.js +0 -144
- package/static/v3/js/views/settings.b7140634.js +0 -317
- package/static/v3/js/views/settings.js +0 -317
- package/static/v3/js/views/skills.c6c2f965.js +0 -109
- package/static/v3/js/views/skills.js +0 -109
- package/static/v3/js/views/snapshots.6f5db095.js +0 -135
- package/static/v3/js/views/snapshots.js +0 -135
- package/static/v3/js/views/tools.e4f11276.js +0 -108
- package/static/v3/js/views/tools.js +0 -108
- package/static/v3/js/views/workflows.7752225a.js +0 -213
- package/static/v3/js/views/workflows.js +0 -213
- package/static/v3/js/views/workspace-admin.c466029b.js +0 -156
- package/static/v3/js/views/workspace-admin.js +0 -156
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Export the FastAPI OpenAPI schema used by the desktop frontend client."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
12
|
+
sys.path.insert(0, str(REPO))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> int:
|
|
16
|
+
from latticeai.app_factory import create_app
|
|
17
|
+
|
|
18
|
+
target = Path(sys.argv[1] if len(sys.argv) > 1 else "frontend/openapi.json")
|
|
19
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
os.environ.setdefault("LATTICEAI_REQUIRE_AUTH", "false")
|
|
21
|
+
os.environ.setdefault("LATTICEAI_TUNNEL", "false")
|
|
22
|
+
os.environ.setdefault("LATTICEAI_AUTOLOAD_MODELS", "false")
|
|
23
|
+
app = create_app()
|
|
24
|
+
schema = app.openapi()
|
|
25
|
+
target.write_text(json.dumps(schema, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
26
|
+
print(f"wrote {target} with {len(schema.get('paths', {}))} paths")
|
|
27
|
+
return 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
const repo = join(import.meta.dirname, "..");
|
|
7
|
+
const frontend = join(repo, "frontend");
|
|
8
|
+
const staticRoot = join(repo, "static");
|
|
9
|
+
let failures = 0;
|
|
10
|
+
|
|
11
|
+
function fail(message) {
|
|
12
|
+
failures += 1;
|
|
13
|
+
console.error(`FAIL ${message}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function walk(dir, exts) {
|
|
17
|
+
if (!existsSync(dir)) return [];
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const name of readdirSync(dir)) {
|
|
20
|
+
const path = join(dir, name);
|
|
21
|
+
const stat = statSync(path);
|
|
22
|
+
if (stat.isDirectory()) {
|
|
23
|
+
if (name === "node_modules") continue;
|
|
24
|
+
out.push(...walk(path, exts));
|
|
25
|
+
} else if (exts.some((ext) => name.endsWith(ext))) {
|
|
26
|
+
out.push(path);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const tsc = spawnSync("npx", ["tsc", "-p", "tsconfig.json", "--noEmit"], { cwd: repo, encoding: "utf8" });
|
|
33
|
+
if (tsc.status !== 0) fail(`frontend typecheck\n${tsc.stdout}${tsc.stderr}`);
|
|
34
|
+
else console.log("typecheck: frontend TS passes");
|
|
35
|
+
|
|
36
|
+
const files = [
|
|
37
|
+
...walk(frontend, [".ts", ".tsx", ".css", ".html"]),
|
|
38
|
+
...walk(join(staticRoot, "app"), [".js", ".css", ".html", ".json"]),
|
|
39
|
+
join(staticRoot, "sw.js"),
|
|
40
|
+
join(staticRoot, "manifest.json"),
|
|
41
|
+
].filter(existsSync);
|
|
42
|
+
|
|
43
|
+
const external = /https?:\/\/(fonts\.googleapis\.com|fonts\.gstatic\.com|cdn\.jsdelivr\.net|unpkg\.com|cdnjs\.cloudflare\.com)/;
|
|
44
|
+
const stale = /static\/v3|lint_v3|build_v3_assets/;
|
|
45
|
+
let scanned = 0;
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
scanned += 1;
|
|
48
|
+
const text = readFileSync(file, "utf8");
|
|
49
|
+
if (external.test(text)) fail(`${relative(repo, file)}: CDN reference`);
|
|
50
|
+
if (stale.test(text)) fail(`${relative(repo, file)}: stale v3 frontend reference`);
|
|
51
|
+
}
|
|
52
|
+
console.log(`privacy: ${scanned} frontend/static files scanned`);
|
|
53
|
+
|
|
54
|
+
const client = readFileSync(join(frontend, "src", "api", "client.ts"), "utf8");
|
|
55
|
+
if (!client.includes("openapi-fetch") || !client.includes("./openapi")) {
|
|
56
|
+
fail("frontend/src/api/client.ts must use the generated OpenAPI client");
|
|
57
|
+
}
|
|
58
|
+
if (!existsSync(join(frontend, "src", "api", "openapi.ts"))) {
|
|
59
|
+
fail("generated OpenAPI types missing");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const openapi = JSON.parse(readFileSync(join(frontend, "openapi.json"), "utf8"));
|
|
63
|
+
const openapiPaths = Object.keys(openapi.paths || {});
|
|
64
|
+
const requiredPaths = [
|
|
65
|
+
"/health",
|
|
66
|
+
"/chat",
|
|
67
|
+
"/api/graph",
|
|
68
|
+
"/api/search/hybrid",
|
|
69
|
+
"/api/knowledge-graph/export",
|
|
70
|
+
"/api/memory/recall",
|
|
71
|
+
"/agents/api/run",
|
|
72
|
+
"/workflows/api/definitions",
|
|
73
|
+
"/workspace/os",
|
|
74
|
+
"/models",
|
|
75
|
+
"/api/brain/storage",
|
|
76
|
+
"/api/brain/storage/postgres/docker",
|
|
77
|
+
"/api/brain/storage/migrate-postgres",
|
|
78
|
+
"/api/knowledge-graph/archive",
|
|
79
|
+
"/api/knowledge-graph/archive/restore",
|
|
80
|
+
];
|
|
81
|
+
if (openapiPaths.length < 300) fail(`OpenAPI path count too low: ${openapiPaths.length}`);
|
|
82
|
+
for (const path of requiredPaths) {
|
|
83
|
+
if (!openapiPaths.includes(path)) fail(`OpenAPI path missing: ${path}`);
|
|
84
|
+
}
|
|
85
|
+
console.log(`api: generated OpenAPI schema exposes ${openapiPaths.length} paths`);
|
|
86
|
+
|
|
87
|
+
if (failures) {
|
|
88
|
+
console.error(`\nfrontend lint: ${failures} failure(s)`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
console.log("\nfrontend lint: all checks pass");
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Brain storage migration utility.
|
|
3
|
+
|
|
4
|
+
Examples:
|
|
5
|
+
python scripts/migrate_brain_storage.py plan --sqlite ~/.ltcai/knowledge_graph.sqlite --dsn postgresql://...
|
|
6
|
+
python scripts/migrate_brain_storage.py migrate --sqlite ~/.ltcai/knowledge_graph.sqlite --dsn postgresql://...
|
|
7
|
+
python scripts/migrate_brain_storage.py docker-plan --data-dir ~/.ltcai/postgres
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from lattice_brain.storage import DockerPostgresWizard, PostgresEngine, SQLiteToPostgresMigrator
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _print_json(value: object) -> None:
|
|
20
|
+
print(json.dumps(value, ensure_ascii=False, indent=2))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> int:
|
|
24
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
25
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
26
|
+
|
|
27
|
+
for name in ("plan", "migrate"):
|
|
28
|
+
p = sub.add_parser(name)
|
|
29
|
+
p.add_argument("--sqlite", type=Path, required=True)
|
|
30
|
+
p.add_argument("--dsn", required=True)
|
|
31
|
+
p.add_argument("--schema", default="lattice_brain")
|
|
32
|
+
|
|
33
|
+
docker = sub.add_parser("docker-plan")
|
|
34
|
+
docker.add_argument("--data-dir", type=Path, required=True)
|
|
35
|
+
docker.add_argument("--port", type=int, default=5432)
|
|
36
|
+
|
|
37
|
+
args = parser.parse_args()
|
|
38
|
+
if args.command == "docker-plan":
|
|
39
|
+
wizard = DockerPostgresWizard(args.data_dir, port=args.port)
|
|
40
|
+
result = wizard.start(consent=False)
|
|
41
|
+
_print_json(result)
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
migrator = SQLiteToPostgresMigrator(
|
|
45
|
+
args.sqlite,
|
|
46
|
+
PostgresEngine(args.dsn, schema=args.schema),
|
|
47
|
+
)
|
|
48
|
+
_print_json(migrator.migrate(dry_run=args.command == "plan"))
|
|
49
|
+
return 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
if (args.length === 0) {
|
|
9
|
+
console.error("usage: node scripts/run_python.mjs <python-args...>");
|
|
10
|
+
process.exit(2);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const candidates = [
|
|
14
|
+
process.env.LTCAI_PYTHON,
|
|
15
|
+
path.join(process.cwd(), ".venv", "bin", "python"),
|
|
16
|
+
path.join(process.cwd(), ".venv", "Scripts", "python.exe"),
|
|
17
|
+
"python3",
|
|
18
|
+
"python",
|
|
19
|
+
].filter(Boolean);
|
|
20
|
+
|
|
21
|
+
let lastError = null;
|
|
22
|
+
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
const isPath = candidate.includes(path.sep);
|
|
25
|
+
if (isPath && !existsSync(candidate)) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = spawnSync(candidate, args, {
|
|
30
|
+
cwd: process.cwd(),
|
|
31
|
+
env: process.env,
|
|
32
|
+
stdio: "inherit",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (result.error) {
|
|
36
|
+
if (result.error.code === "ENOENT") {
|
|
37
|
+
lastError = result.error;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
throw result.error;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
process.exit(result.status ?? 1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.error(`No usable Python executable found. Last error: ${lastError?.message ?? "none"}`);
|
|
47
|
+
process.exit(127);
|
package/scripts/wheel_smoke.py
CHANGED
|
@@ -33,6 +33,9 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
|
33
33
|
# Every importable module the wheel ships (pyproject py-modules + packages).
|
|
34
34
|
WHEEL_MODULES = [
|
|
35
35
|
"setup_wizard",
|
|
36
|
+
"lattice_brain",
|
|
37
|
+
"lattice_brain.storage",
|
|
38
|
+
"lattice_brain.archive",
|
|
36
39
|
"latticeai",
|
|
37
40
|
"latticeai.server_app",
|
|
38
41
|
"latticeai.app_factory",
|