ltcai 0.3.2 → 0.5.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 +285 -224
- package/docs/CHANGELOG.md +60 -0
- package/kg_schema.py +42 -0
- package/knowledge_graph.py +232 -36
- package/latticeai/core/agent.py +453 -0
- package/latticeai/core/config.py +178 -0
- package/llm_router.py +20 -8
- package/package.json +1 -1
- package/server.py +92 -436
- package/tools.py +87 -115
package/tools.py
CHANGED
|
@@ -17,7 +17,7 @@ import tempfile
|
|
|
17
17
|
import json
|
|
18
18
|
from html.parser import HTMLParser
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Any, Dict, List, Optional
|
|
20
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
21
21
|
|
|
22
22
|
_PLATFORM = platform.system() # "Darwin" | "Windows" | "Linux"
|
|
23
23
|
|
|
@@ -1435,118 +1435,90 @@ def git_show(revision: str = "HEAD", cwd: Optional[str] = None) -> Dict[str, Any
|
|
|
1435
1435
|
return _run_git(["show", "--stat", "--oneline", "--decorate", revision], cwd)
|
|
1436
1436
|
|
|
1437
1437
|
|
|
1438
|
+
def _h_create_xlsx(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
1439
|
+
rows = args.get("rows", [])
|
|
1440
|
+
if isinstance(rows, str):
|
|
1441
|
+
rows = json.loads(rows)
|
|
1442
|
+
return create_xlsx(rows, args.get("filename", "spreadsheet.xlsx"), args.get("sheet_name", "Sheet1"))
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
def _h_create_pptx(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
1446
|
+
slides = args.get("slides", [])
|
|
1447
|
+
if isinstance(slides, str):
|
|
1448
|
+
slides = json.loads(slides)
|
|
1449
|
+
return create_pptx(args.get("title", ""), slides, args.get("filename", "presentation.pptx"))
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
# ── Tool registry: the single source of truth for name → invocation ───────────
|
|
1453
|
+
# Each entry binds the args dict to a tool function. ``execute_tool`` is a
|
|
1454
|
+
# lookup over this table — adding a tool means adding one entry here, not
|
|
1455
|
+
# editing an if/elif chain. server.py's governance map and catalog brief are
|
|
1456
|
+
# checked against ``registered_tools()`` so the three never silently drift.
|
|
1457
|
+
TOOL_HANDLERS: Dict[str, Callable[[Dict[str, Any]], Dict[str, Any]]] = {
|
|
1458
|
+
# filesystem
|
|
1459
|
+
"list_dir": lambda a: list_dir(a.get("path", ".")),
|
|
1460
|
+
"workspace_tree": lambda a: workspace_tree(a.get("path", "."), a.get("max_depth", 3)),
|
|
1461
|
+
"read_file": lambda a: read_file(a["path"], offset=a.get("offset", 0), limit=a.get("limit", 0), line_numbers=a.get("line_numbers", True)),
|
|
1462
|
+
"write_file": lambda a: write_file(a["path"], a.get("content", "")),
|
|
1463
|
+
"edit_file": lambda a: edit_file(a["path"], a["old_string"], a["new_string"], replace_all=bool(a.get("replace_all", False))),
|
|
1464
|
+
"grep": lambda a: grep(a["pattern"], path=a.get("path", "."), glob=a.get("glob"), max_results=a.get("max_results", 50), case_insensitive=bool(a.get("case_insensitive", False)), context_lines=a.get("context_lines", 0)),
|
|
1465
|
+
"search_files": lambda a: search_files(a["query"], a.get("path", "."), a.get("max_results", 20)),
|
|
1466
|
+
"inspect_html": lambda a: inspect_html(a["path"]),
|
|
1467
|
+
"preview_url": lambda a: preview_url(a.get("path", "index.html")),
|
|
1468
|
+
# planning
|
|
1469
|
+
"todo_read": lambda a: todo_read(),
|
|
1470
|
+
"todo_write": lambda a: todo_write(a.get("todos") or []),
|
|
1471
|
+
# documents
|
|
1472
|
+
"create_docx": lambda a: create_docx(a.get("title", ""), a.get("body", ""), a.get("filename", "document.docx")),
|
|
1473
|
+
"create_xlsx": _h_create_xlsx,
|
|
1474
|
+
"create_pptx": _h_create_pptx,
|
|
1475
|
+
"create_pdf": lambda a: create_pdf(a.get("title", ""), a.get("body", ""), a.get("filename", "document.pdf")),
|
|
1476
|
+
"create_web_project": lambda a: create_web_project(a.get("path", ""), a.get("framework", "react"), a.get("template", "vite")),
|
|
1477
|
+
# local filesystem
|
|
1478
|
+
"local_list": lambda a: local_list(a["path"]),
|
|
1479
|
+
"local_read": lambda a: local_read(a["path"]),
|
|
1480
|
+
"local_write": lambda a: local_write(a["path"], a.get("content", "")),
|
|
1481
|
+
"read_document": lambda a: read_document(a["path"]),
|
|
1482
|
+
"network_status": lambda a: network_status(),
|
|
1483
|
+
# computer use
|
|
1484
|
+
"computer_screenshot": lambda a: computer_screenshot(),
|
|
1485
|
+
"computer_open_app": lambda a: computer_open_app(a.get("app", "Google Chrome")),
|
|
1486
|
+
"computer_open_url": lambda a: computer_open_url(a["url"], a.get("app", "Google Chrome")),
|
|
1487
|
+
"computer_click": lambda a: computer_click(a.get("x", 0), a.get("y", 0), a.get("button", "left"), a.get("double", False)),
|
|
1488
|
+
"computer_type": lambda a: computer_type(a["text"], a.get("interval", 0.04)),
|
|
1489
|
+
"computer_key": lambda a: computer_key(a["key"]),
|
|
1490
|
+
"computer_scroll": lambda a: computer_scroll(a.get("x", 0), a.get("y", 0), a.get("direction", "down"), a.get("clicks", 3)),
|
|
1491
|
+
"computer_move": lambda a: computer_move(a.get("x", 0), a.get("y", 0)),
|
|
1492
|
+
"computer_drag": lambda a: computer_drag(a.get("x1", 0), a.get("y1", 0), a.get("x2", 0), a.get("y2", 0)),
|
|
1493
|
+
"computer_status": lambda a: computer_status(),
|
|
1494
|
+
"chrome_status": lambda a: desktop_bridge_status(),
|
|
1495
|
+
"computer_use_status": lambda a: desktop_bridge_status(),
|
|
1496
|
+
# knowledge / obsidian
|
|
1497
|
+
"knowledge_save": lambda a: knowledge_save(a["content"], a.get("folder", "00_Raw"), a.get("title")),
|
|
1498
|
+
"knowledge_search": lambda a: knowledge_search(a["query"], a.get("max_results", 5)),
|
|
1499
|
+
"knowledge_tree": lambda a: knowledge_tree(),
|
|
1500
|
+
"obsidian_save": lambda a: obsidian_save(a["content"], a.get("folder", "00_Raw"), a.get("title")),
|
|
1501
|
+
"obsidian_search": lambda a: obsidian_search(a["query"], a.get("max_results", 5)),
|
|
1502
|
+
"obsidian_tree": lambda a: obsidian_tree(),
|
|
1503
|
+
# git (read-only)
|
|
1504
|
+
"git_status": lambda a: git_status(a.get("cwd")),
|
|
1505
|
+
"git_diff": lambda a: git_diff(a.get("path"), a.get("cwd")),
|
|
1506
|
+
"git_log": lambda a: git_log(a.get("max_count", 5), a.get("cwd")),
|
|
1507
|
+
"git_show": lambda a: git_show(a.get("revision", "HEAD"), a.get("cwd")),
|
|
1508
|
+
# exec
|
|
1509
|
+
"run_command": lambda a: run_command(a["command"], a.get("cwd")),
|
|
1510
|
+
"build_project": lambda a: build_project(a.get("cwd"), a.get("script", "build")),
|
|
1511
|
+
"deploy_project": lambda a: deploy_project(a.get("cwd"), a.get("script", "deploy")),
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
def registered_tools() -> frozenset:
|
|
1516
|
+
"""Names dispatchable through ``execute_tool`` — the seam other modules verify against."""
|
|
1517
|
+
return frozenset(TOOL_HANDLERS)
|
|
1518
|
+
|
|
1519
|
+
|
|
1438
1520
|
def execute_tool(action: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
if action == "read_file":
|
|
1444
|
-
return read_file(
|
|
1445
|
-
args["path"],
|
|
1446
|
-
offset=args.get("offset", 0),
|
|
1447
|
-
limit=args.get("limit", 0),
|
|
1448
|
-
line_numbers=args.get("line_numbers", True),
|
|
1449
|
-
)
|
|
1450
|
-
if action == "write_file":
|
|
1451
|
-
return write_file(args["path"], args.get("content", ""))
|
|
1452
|
-
if action == "edit_file":
|
|
1453
|
-
return edit_file(
|
|
1454
|
-
args["path"],
|
|
1455
|
-
args["old_string"],
|
|
1456
|
-
args["new_string"],
|
|
1457
|
-
replace_all=bool(args.get("replace_all", False)),
|
|
1458
|
-
)
|
|
1459
|
-
if action == "grep":
|
|
1460
|
-
return grep(
|
|
1461
|
-
args["pattern"],
|
|
1462
|
-
path=args.get("path", "."),
|
|
1463
|
-
glob=args.get("glob"),
|
|
1464
|
-
max_results=args.get("max_results", 50),
|
|
1465
|
-
case_insensitive=bool(args.get("case_insensitive", False)),
|
|
1466
|
-
context_lines=args.get("context_lines", 0),
|
|
1467
|
-
)
|
|
1468
|
-
if action == "search_files":
|
|
1469
|
-
return search_files(args["query"], args.get("path", "."), args.get("max_results", 20))
|
|
1470
|
-
if action == "todo_read":
|
|
1471
|
-
return todo_read()
|
|
1472
|
-
if action == "todo_write":
|
|
1473
|
-
return todo_write(args.get("todos") or [])
|
|
1474
|
-
if action == "inspect_html":
|
|
1475
|
-
return inspect_html(args["path"])
|
|
1476
|
-
if action == "preview_url":
|
|
1477
|
-
return preview_url(args.get("path", "index.html"))
|
|
1478
|
-
if action == "create_docx":
|
|
1479
|
-
return create_docx(args.get("title", ""), args.get("body", ""), args.get("filename", "document.docx"))
|
|
1480
|
-
if action == "create_xlsx":
|
|
1481
|
-
rows = args.get("rows", [])
|
|
1482
|
-
if isinstance(rows, str):
|
|
1483
|
-
rows = json.loads(rows)
|
|
1484
|
-
return create_xlsx(rows, args.get("filename", "spreadsheet.xlsx"), args.get("sheet_name", "Sheet1"))
|
|
1485
|
-
if action == "create_pptx":
|
|
1486
|
-
slides = args.get("slides", [])
|
|
1487
|
-
if isinstance(slides, str):
|
|
1488
|
-
slides = json.loads(slides)
|
|
1489
|
-
return create_pptx(args.get("title", ""), slides, args.get("filename", "presentation.pptx"))
|
|
1490
|
-
if action == "create_pdf":
|
|
1491
|
-
return create_pdf(args.get("title", ""), args.get("body", ""), args.get("filename", "document.pdf"))
|
|
1492
|
-
if action == "create_web_project":
|
|
1493
|
-
return create_web_project(args.get("path", ""), args.get("framework", "react"), args.get("template", "vite"))
|
|
1494
|
-
if action == "local_list":
|
|
1495
|
-
return local_list(args["path"])
|
|
1496
|
-
if action == "local_read":
|
|
1497
|
-
return local_read(args["path"])
|
|
1498
|
-
if action == "local_write":
|
|
1499
|
-
return local_write(args["path"], args.get("content", ""))
|
|
1500
|
-
if action == "read_document":
|
|
1501
|
-
return read_document(args["path"])
|
|
1502
|
-
if action == "network_status":
|
|
1503
|
-
return network_status()
|
|
1504
|
-
if action == "computer_screenshot":
|
|
1505
|
-
return computer_screenshot()
|
|
1506
|
-
if action == "computer_open_app":
|
|
1507
|
-
return computer_open_app(args.get("app", "Google Chrome"))
|
|
1508
|
-
if action == "computer_open_url":
|
|
1509
|
-
return computer_open_url(args["url"], args.get("app", "Google Chrome"))
|
|
1510
|
-
if action == "computer_click":
|
|
1511
|
-
return computer_click(args.get("x", 0), args.get("y", 0), args.get("button", "left"), args.get("double", False))
|
|
1512
|
-
if action == "computer_type":
|
|
1513
|
-
return computer_type(args["text"], args.get("interval", 0.04))
|
|
1514
|
-
if action == "computer_key":
|
|
1515
|
-
return computer_key(args["key"])
|
|
1516
|
-
if action == "computer_scroll":
|
|
1517
|
-
return computer_scroll(args.get("x", 0), args.get("y", 0), args.get("direction", "down"), args.get("clicks", 3))
|
|
1518
|
-
if action == "computer_move":
|
|
1519
|
-
return computer_move(args.get("x", 0), args.get("y", 0))
|
|
1520
|
-
if action == "computer_drag":
|
|
1521
|
-
return computer_drag(args.get("x1", 0), args.get("y1", 0), args.get("x2", 0), args.get("y2", 0))
|
|
1522
|
-
if action == "computer_status":
|
|
1523
|
-
return computer_status()
|
|
1524
|
-
if action in {"chrome_status", "computer_use_status"}:
|
|
1525
|
-
return desktop_bridge_status()
|
|
1526
|
-
if action == "knowledge_save":
|
|
1527
|
-
return knowledge_save(args["content"], args.get("folder", "00_Raw"), args.get("title"))
|
|
1528
|
-
if action == "knowledge_search":
|
|
1529
|
-
return knowledge_search(args["query"], args.get("max_results", 5))
|
|
1530
|
-
if action == "knowledge_tree":
|
|
1531
|
-
return knowledge_tree()
|
|
1532
|
-
if action == "obsidian_save":
|
|
1533
|
-
return obsidian_save(args["content"], args.get("folder", "00_Raw"), args.get("title"))
|
|
1534
|
-
if action == "obsidian_search":
|
|
1535
|
-
return obsidian_search(args["query"], args.get("max_results", 5))
|
|
1536
|
-
if action == "obsidian_tree":
|
|
1537
|
-
return obsidian_tree()
|
|
1538
|
-
if action == "git_status":
|
|
1539
|
-
return git_status(args.get("cwd"))
|
|
1540
|
-
if action == "git_diff":
|
|
1541
|
-
return git_diff(args.get("path"), args.get("cwd"))
|
|
1542
|
-
if action == "git_log":
|
|
1543
|
-
return git_log(args.get("max_count", 5), args.get("cwd"))
|
|
1544
|
-
if action == "git_show":
|
|
1545
|
-
return git_show(args.get("revision", "HEAD"), args.get("cwd"))
|
|
1546
|
-
if action == "run_command":
|
|
1547
|
-
return run_command(args["command"], args.get("cwd"))
|
|
1548
|
-
if action == "build_project":
|
|
1549
|
-
return build_project(args.get("cwd"), args.get("script", "build"))
|
|
1550
|
-
if action == "deploy_project":
|
|
1551
|
-
return deploy_project(args.get("cwd"), args.get("script", "deploy"))
|
|
1552
|
-
raise ToolError(f"Unknown action: {action}")
|
|
1521
|
+
handler = TOOL_HANDLERS.get(action)
|
|
1522
|
+
if handler is None:
|
|
1523
|
+
raise ToolError(f"Unknown action: {action}")
|
|
1524
|
+
return handler(args)
|