skilllite 0.1.1__tar.gz → 0.1.2__tar.gz

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.
Files changed (52) hide show
  1. {skilllite-0.1.1/skilllite.egg-info → skilllite-0.1.2}/PKG-INFO +98 -1
  2. {skilllite-0.1.1 → skilllite-0.1.2}/README.md +91 -0
  3. {skilllite-0.1.1 → skilllite-0.1.2}/pyproject.toml +5 -1
  4. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/__init__.py +2 -0
  5. skilllite-0.1.2/skilllite/core/adapters/__init__.py +74 -0
  6. skilllite-0.1.2/skilllite/core/adapters/langchain.py +362 -0
  7. skilllite-0.1.2/skilllite/core/adapters/llamaindex.py +264 -0
  8. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/handler.py +179 -4
  9. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/loops.py +175 -13
  10. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/manager.py +82 -15
  11. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/metadata.py +14 -7
  12. skilllite-0.1.2/skilllite/core/security.py +420 -0
  13. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/mcp/server.py +266 -58
  14. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/quick.py +14 -4
  15. skilllite-0.1.2/skilllite/sandbox/context.py +155 -0
  16. skilllite-0.1.2/skilllite/sandbox/execution_service.py +254 -0
  17. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/skillbox/executor.py +124 -19
  18. skilllite-0.1.2/skilllite/sandbox/unified_executor.py +359 -0
  19. {skilllite-0.1.1 → skilllite-0.1.2/skilllite.egg-info}/PKG-INFO +98 -1
  20. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/SOURCES.txt +7 -0
  21. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/requires.txt +8 -0
  22. {skilllite-0.1.1 → skilllite-0.1.2}/LICENSE +0 -0
  23. {skilllite-0.1.1 → skilllite-0.1.2}/MANIFEST.in +0 -0
  24. {skilllite-0.1.1 → skilllite-0.1.2}/setup.cfg +0 -0
  25. {skilllite-0.1.1 → skilllite-0.1.2}/setup.py +0 -0
  26. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/__init__.py +0 -0
  27. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/analyzer.py +0 -0
  28. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/builtin_tools.py +0 -0
  29. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/__init__.py +0 -0
  30. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/__main__.py +0 -0
  31. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/binary.py +0 -0
  32. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/integrations/__init__.py +0 -0
  33. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/integrations/opencode.py +0 -0
  34. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/main.py +0 -0
  35. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/mcp.py +0 -0
  36. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/executor.py +0 -0
  37. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/prompt_builder.py +0 -0
  38. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/registry.py +0 -0
  39. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/skill_info.py +0 -0
  40. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/tool_builder.py +0 -0
  41. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/tools.py +0 -0
  42. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/mcp/__init__.py +0 -0
  43. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/__init__.py +0 -0
  44. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/base.py +0 -0
  45. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/config.py +0 -0
  46. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/skillbox/__init__.py +0 -0
  47. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/skillbox/binary.py +0 -0
  48. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/utils.py +0 -0
  49. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/validation.py +0 -0
  50. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/dependency_links.txt +0 -0
  51. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/entry_points.txt +0 -0
  52. {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skilllite
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A lightweight Skills execution engine with LLM integration for LLM agents
5
5
  Author-email: SkillLite Team <skilllite@example.com>
6
6
  License: MIT
@@ -32,10 +32,16 @@ Provides-Extra: anthropic
32
32
  Requires-Dist: anthropic>=0.18.0; extra == "anthropic"
33
33
  Provides-Extra: mcp
34
34
  Requires-Dist: mcp>=1.0.0; extra == "mcp"
35
+ Provides-Extra: langchain
36
+ Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
37
+ Provides-Extra: llamaindex
38
+ Requires-Dist: llama-index-core>=0.10.0; extra == "llamaindex"
35
39
  Provides-Extra: all
36
40
  Requires-Dist: openai>=1.0.0; extra == "all"
37
41
  Requires-Dist: anthropic>=0.18.0; extra == "all"
38
42
  Requires-Dist: mcp>=1.0.0; extra == "all"
43
+ Requires-Dist: langchain-core>=0.1.0; extra == "all"
44
+ Requires-Dist: llama-index-core>=0.10.0; extra == "all"
39
45
  Provides-Extra: dev
40
46
  Requires-Dist: pytest>=7.0; extra == "dev"
41
47
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -288,6 +294,97 @@ Enum for LLM provider formats:
288
294
  - `ToolFormat.CLAUDE`
289
295
  - `ToolFormat.OPENAI`
290
296
 
297
+ ## Framework Adapters
298
+
299
+ SkillLite provides adapters for popular AI frameworks with security confirmation support.
300
+
301
+ ### LangChain Integration
302
+
303
+ For LangChain/LangGraph integration, we recommend using the dedicated **[langchain-skilllite](https://pypi.org/project/langchain-skilllite/)** package:
304
+
305
+ ```bash
306
+ pip install langchain-skilllite
307
+ ```
308
+
309
+ ```python
310
+ from langchain_skilllite import SkillLiteToolkit
311
+ from langchain_openai import ChatOpenAI
312
+ from langgraph.prebuilt import create_react_agent
313
+
314
+ # Load all skills from a directory as LangChain tools
315
+ tools = SkillLiteToolkit.from_directory("./skills")
316
+
317
+ # Create a LangGraph agent
318
+ agent = create_react_agent(ChatOpenAI(model="gpt-4"), tools)
319
+
320
+ # Run the agent
321
+ result = agent.invoke({
322
+ "messages": [("user", "Convert 'hello world' to uppercase")]
323
+ })
324
+ ```
325
+
326
+ With security confirmation (sandbox_level=3):
327
+
328
+ ```python
329
+ def confirm_execution(report: str, scan_id: str) -> bool:
330
+ print(report)
331
+ return input("Continue? [y/N]: ").lower() == 'y'
332
+
333
+ tools = SkillLiteToolkit.from_directory(
334
+ "./skills",
335
+ sandbox_level=3, # 1=no sandbox, 2=sandbox only, 3=sandbox+scan
336
+ confirmation_callback=confirm_execution
337
+ )
338
+ ```
339
+
340
+ For more details, see the [langchain-skilllite documentation](../langchain-skilllite/README.md).
341
+
342
+ **Alternative**: You can also use the built-in adapter:
343
+
344
+ ```python
345
+ from skilllite import SkillManager
346
+ from skilllite.core.adapters.langchain import SkillLiteToolkit
347
+
348
+ manager = SkillManager(skills_dir="./skills")
349
+ tools = SkillLiteToolkit.from_manager(manager).get_tools()
350
+ ```
351
+
352
+ ### LlamaIndex Integration
353
+
354
+ ```python
355
+ from skilllite import SkillManager
356
+ from skilllite.core.adapters.llamaindex import SkillLiteToolSpec
357
+
358
+ manager = SkillManager(skills_dir="./skills")
359
+
360
+ # Basic usage
361
+ tool_spec = SkillLiteToolSpec.from_manager(manager)
362
+ tools = tool_spec.to_tool_list()
363
+
364
+ # With security confirmation
365
+ def confirm(report: str, scan_id: str) -> bool:
366
+ print(report)
367
+ return input("Continue? [y/N]: ").lower() == 'y'
368
+
369
+ tool_spec = SkillLiteToolSpec.from_manager(
370
+ manager,
371
+ sandbox_level=3,
372
+ confirmation_callback=confirm
373
+ )
374
+
375
+ # Use with LlamaIndex agent
376
+ from llama_index.core.agent import ReActAgent
377
+ agent = ReActAgent.from_tools(tools, llm=llm)
378
+ ```
379
+
380
+ ### Security Levels
381
+
382
+ | Level | Description |
383
+ |-------|-------------|
384
+ | 1 | No sandbox - direct execution |
385
+ | 2 | Sandbox isolation only |
386
+ | 3 | Sandbox + static security scan (requires confirmation for high-severity issues) |
387
+
291
388
  ## OpenCode Integration
292
389
 
293
390
  SkillLite can be integrated with [OpenCode](https://github.com/opencode-ai/opencode) as an MCP (Model Context Protocol) server, providing secure sandbox execution capabilities.
@@ -242,6 +242,97 @@ Enum for LLM provider formats:
242
242
  - `ToolFormat.CLAUDE`
243
243
  - `ToolFormat.OPENAI`
244
244
 
245
+ ## Framework Adapters
246
+
247
+ SkillLite provides adapters for popular AI frameworks with security confirmation support.
248
+
249
+ ### LangChain Integration
250
+
251
+ For LangChain/LangGraph integration, we recommend using the dedicated **[langchain-skilllite](https://pypi.org/project/langchain-skilllite/)** package:
252
+
253
+ ```bash
254
+ pip install langchain-skilllite
255
+ ```
256
+
257
+ ```python
258
+ from langchain_skilllite import SkillLiteToolkit
259
+ from langchain_openai import ChatOpenAI
260
+ from langgraph.prebuilt import create_react_agent
261
+
262
+ # Load all skills from a directory as LangChain tools
263
+ tools = SkillLiteToolkit.from_directory("./skills")
264
+
265
+ # Create a LangGraph agent
266
+ agent = create_react_agent(ChatOpenAI(model="gpt-4"), tools)
267
+
268
+ # Run the agent
269
+ result = agent.invoke({
270
+ "messages": [("user", "Convert 'hello world' to uppercase")]
271
+ })
272
+ ```
273
+
274
+ With security confirmation (sandbox_level=3):
275
+
276
+ ```python
277
+ def confirm_execution(report: str, scan_id: str) -> bool:
278
+ print(report)
279
+ return input("Continue? [y/N]: ").lower() == 'y'
280
+
281
+ tools = SkillLiteToolkit.from_directory(
282
+ "./skills",
283
+ sandbox_level=3, # 1=no sandbox, 2=sandbox only, 3=sandbox+scan
284
+ confirmation_callback=confirm_execution
285
+ )
286
+ ```
287
+
288
+ For more details, see the [langchain-skilllite documentation](../langchain-skilllite/README.md).
289
+
290
+ **Alternative**: You can also use the built-in adapter:
291
+
292
+ ```python
293
+ from skilllite import SkillManager
294
+ from skilllite.core.adapters.langchain import SkillLiteToolkit
295
+
296
+ manager = SkillManager(skills_dir="./skills")
297
+ tools = SkillLiteToolkit.from_manager(manager).get_tools()
298
+ ```
299
+
300
+ ### LlamaIndex Integration
301
+
302
+ ```python
303
+ from skilllite import SkillManager
304
+ from skilllite.core.adapters.llamaindex import SkillLiteToolSpec
305
+
306
+ manager = SkillManager(skills_dir="./skills")
307
+
308
+ # Basic usage
309
+ tool_spec = SkillLiteToolSpec.from_manager(manager)
310
+ tools = tool_spec.to_tool_list()
311
+
312
+ # With security confirmation
313
+ def confirm(report: str, scan_id: str) -> bool:
314
+ print(report)
315
+ return input("Continue? [y/N]: ").lower() == 'y'
316
+
317
+ tool_spec = SkillLiteToolSpec.from_manager(
318
+ manager,
319
+ sandbox_level=3,
320
+ confirmation_callback=confirm
321
+ )
322
+
323
+ # Use with LlamaIndex agent
324
+ from llama_index.core.agent import ReActAgent
325
+ agent = ReActAgent.from_tools(tools, llm=llm)
326
+ ```
327
+
328
+ ### Security Levels
329
+
330
+ | Level | Description |
331
+ |-------|-------------|
332
+ | 1 | No sandbox - direct execution |
333
+ | 2 | Sandbox isolation only |
334
+ | 3 | Sandbox + static security scan (requires confirmation for high-severity issues) |
335
+
245
336
  ## OpenCode Integration
246
337
 
247
338
  SkillLite can be integrated with [OpenCode](https://github.com/opencode-ai/opencode) as an MCP (Model Context Protocol) server, providing secure sandbox execution capabilities.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "skilllite"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "A lightweight Skills execution engine with LLM integration for LLM agents"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -36,10 +36,14 @@ dependencies = [
36
36
  openai = ["openai>=1.0.0"]
37
37
  anthropic = ["anthropic>=0.18.0"]
38
38
  mcp = ["mcp>=1.0.0"]
39
+ langchain = ["langchain-core>=0.1.0"]
40
+ llamaindex = ["llama-index-core>=0.10.0"]
39
41
  all = [
40
42
  "openai>=1.0.0",
41
43
  "anthropic>=0.18.0",
42
44
  "mcp>=1.0.0",
45
+ "langchain-core>=0.1.0",
46
+ "llama-index-core>=0.10.0",
43
47
  ]
44
48
  dev = [
45
49
  "pytest>=7.0",
@@ -62,4 +62,6 @@ __all__ = [
62
62
  "ToolFormat",
63
63
  # Utilities
64
64
  "parse_skill_metadata",
65
+ # Adapters (lazy loaded)
66
+ "adapters",
65
67
  ]
@@ -0,0 +1,74 @@
1
+ """
2
+ SkillLite Adapters - Framework adapters for LangChain, LlamaIndex, etc.
3
+
4
+ This module provides adapters for integrating SkillLite with popular AI frameworks:
5
+ - LangChain: SkillLiteTool, SkillLiteToolkit
6
+ - LlamaIndex: SkillLiteToolSpec
7
+
8
+ Both adapters support sandbox security confirmation (sandbox_level=3):
9
+ - SecurityScanResult: Contains scan results with severity counts
10
+ - ConfirmationCallback: Type alias for (report: str, scan_id: str) -> bool
11
+
12
+ Usage:
13
+ # LangChain (requires: pip install skilllite[langchain])
14
+ from skilllite.core.adapters.langchain import SkillLiteTool, SkillLiteToolkit
15
+
16
+ # LlamaIndex (requires: pip install skilllite[llamaindex])
17
+ from skilllite.core.adapters.llamaindex import SkillLiteToolSpec
18
+
19
+ # Security confirmation callback
20
+ def confirm(report: str, scan_id: str) -> bool:
21
+ print(report)
22
+ return input("Continue? [y/N]: ").lower() == 'y'
23
+
24
+ toolkit = SkillLiteToolkit.from_manager(
25
+ manager, sandbox_level=3, confirmation_callback=confirm
26
+ )
27
+ """
28
+
29
+ __all__ = [
30
+ "SkillLiteTool",
31
+ "SkillLiteToolkit",
32
+ "SkillLiteToolSpec",
33
+ "SecurityScanResult",
34
+ "ConfirmationCallback",
35
+ "AsyncConfirmationCallback",
36
+ ]
37
+
38
+
39
+ def __getattr__(name: str):
40
+ """Lazy import to avoid requiring all dependencies at import time."""
41
+ if name in ("SkillLiteTool", "SkillLiteToolkit", "SecurityScanResult",
42
+ "ConfirmationCallback", "AsyncConfirmationCallback"):
43
+ try:
44
+ from .langchain import (
45
+ SkillLiteTool, SkillLiteToolkit, SecurityScanResult,
46
+ ConfirmationCallback, AsyncConfirmationCallback
47
+ )
48
+ return {
49
+ "SkillLiteTool": SkillLiteTool,
50
+ "SkillLiteToolkit": SkillLiteToolkit,
51
+ "SecurityScanResult": SecurityScanResult,
52
+ "ConfirmationCallback": ConfirmationCallback,
53
+ "AsyncConfirmationCallback": AsyncConfirmationCallback,
54
+ }[name]
55
+ except ImportError as e:
56
+ raise ImportError(
57
+ f"LangChain adapter requires langchain. "
58
+ f"Install with: pip install skilllite[langchain]\n"
59
+ f"Original error: {e}"
60
+ ) from e
61
+
62
+ if name == "SkillLiteToolSpec":
63
+ try:
64
+ from .llamaindex import SkillLiteToolSpec
65
+ return SkillLiteToolSpec
66
+ except ImportError as e:
67
+ raise ImportError(
68
+ f"LlamaIndex adapter requires llama-index. "
69
+ f"Install with: pip install skilllite[llamaindex]\n"
70
+ f"Original error: {e}"
71
+ ) from e
72
+
73
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
74
+
@@ -0,0 +1,362 @@
1
+ """
2
+ LangChain adapter for SkillLite.
3
+
4
+ Provides SkillLiteTool and SkillLiteToolkit for integrating SkillLite
5
+ skills into LangChain agents.
6
+
7
+ Usage:
8
+ from skilllite import SkillManager
9
+ from skilllite.core.adapters.langchain import SkillLiteToolkit
10
+
11
+ manager = SkillManager(skills_dir="./skills")
12
+ tools = SkillLiteToolkit.from_manager(manager)
13
+
14
+ # Use with LangChain agent
15
+ from langchain.agents import create_openai_tools_agent, AgentExecutor
16
+ agent = create_openai_tools_agent(llm, tools, prompt)
17
+ executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools)
18
+
19
+ Security Confirmation:
20
+ For sandbox level 3, the adapter supports security confirmation callbacks:
21
+
22
+ def my_confirmation_callback(security_report: str, scan_id: str) -> bool:
23
+ print(security_report)
24
+ return input("Continue? [y/N]: ").lower() == 'y'
25
+
26
+ tools = SkillLiteToolkit.from_manager(
27
+ manager,
28
+ sandbox_level=3,
29
+ confirmation_callback=my_confirmation_callback
30
+ )
31
+
32
+ Requirements:
33
+ pip install skilllite[langchain]
34
+ """
35
+
36
+ from dataclasses import dataclass, field
37
+ from typing import Any, Callable, Dict, List, Optional, Type, TYPE_CHECKING
38
+ import asyncio
39
+ import time
40
+
41
+ try:
42
+ from langchain_core.tools import BaseTool
43
+ from langchain_core.callbacks import CallbackManagerForToolRun, AsyncCallbackManagerForToolRun
44
+ from pydantic import BaseModel, Field, ConfigDict
45
+ except ImportError as e:
46
+ raise ImportError(
47
+ "LangChain adapter requires langchain-core. "
48
+ "Install with: pip install skilllite[langchain]"
49
+ ) from e
50
+
51
+ if TYPE_CHECKING:
52
+ from ..manager import SkillManager
53
+
54
+
55
+ # Type alias for confirmation callback
56
+ # Signature: (security_report: str, scan_id: str) -> bool
57
+ ConfirmationCallback = Callable[[str, str], bool]
58
+ AsyncConfirmationCallback = Callable[[str, str], "asyncio.Future[bool]"]
59
+
60
+
61
+ @dataclass
62
+ class SecurityScanResult:
63
+ """Result of a security scan for LangChain adapter."""
64
+
65
+ is_safe: bool
66
+ issues: List[Dict[str, Any]] = field(default_factory=list)
67
+ scan_id: str = ""
68
+ code_hash: str = ""
69
+ high_severity_count: int = 0
70
+ medium_severity_count: int = 0
71
+ low_severity_count: int = 0
72
+ timestamp: float = field(default_factory=time.time)
73
+
74
+ @property
75
+ def requires_confirmation(self) -> bool:
76
+ """Check if user confirmation is required."""
77
+ return self.high_severity_count > 0
78
+
79
+ def to_dict(self) -> Dict[str, Any]:
80
+ return {
81
+ "is_safe": self.is_safe,
82
+ "issues": self.issues,
83
+ "scan_id": self.scan_id,
84
+ "code_hash": self.code_hash,
85
+ "high_severity_count": self.high_severity_count,
86
+ "medium_severity_count": self.medium_severity_count,
87
+ "low_severity_count": self.low_severity_count,
88
+ "requires_confirmation": self.requires_confirmation,
89
+ }
90
+
91
+ def format_report(self) -> str:
92
+ """Format a human-readable security report."""
93
+ if not self.issues:
94
+ return "✅ Security scan passed. No issues found."
95
+
96
+ lines = [
97
+ f"📋 Security Scan Report (ID: {self.scan_id[:8]})",
98
+ f" Found {len(self.issues)} item(s) for review:",
99
+ "",
100
+ ]
101
+
102
+ severity_icons = {
103
+ "Critical": "🔴",
104
+ "High": "🟠",
105
+ "Medium": "🟡",
106
+ "Low": "🟢",
107
+ }
108
+
109
+ for idx, issue in enumerate(self.issues, 1):
110
+ severity = issue.get("severity", "Medium")
111
+ icon = severity_icons.get(severity, "⚪")
112
+ lines.append(f" {icon} #{idx} [{severity}] {issue.get('issue_type', 'Unknown')}")
113
+ lines.append(f" ├─ Rule: {issue.get('rule_id', 'N/A')}")
114
+ lines.append(f" ├─ Line {issue.get('line_number', '?')}: {issue.get('description', '')}")
115
+ snippet = issue.get('code_snippet', '')
116
+ lines.append(f" └─ Code: {snippet[:60]}{'...' if len(snippet) > 60 else ''}")
117
+ lines.append("")
118
+
119
+ if self.high_severity_count > 0:
120
+ lines.append("⚠️ High severity issues found. Confirmation required to execute.")
121
+ else:
122
+ lines.append("ℹ️ Only low/medium severity issues found. Safe to execute.")
123
+
124
+ return "\n".join(lines)
125
+
126
+
127
+ class SkillLiteTool(BaseTool):
128
+ """
129
+ LangChain BaseTool adapter for a single SkillLite skill.
130
+
131
+ This wraps a SkillLite skill as a LangChain tool, enabling it to be
132
+ used with LangChain agents.
133
+
134
+ Attributes:
135
+ name: Tool name (same as skill name)
136
+ description: Tool description
137
+ manager: SkillManager instance
138
+ skill_name: Name of the skill to execute
139
+ allow_network: Whether to allow network access
140
+ timeout: Execution timeout in seconds
141
+ sandbox_level: Sandbox security level (1/2/3, default: 3)
142
+ confirmation_callback: Callback for security confirmation (sync)
143
+ async_confirmation_callback: Callback for security confirmation (async)
144
+ """
145
+
146
+ name: str = Field(description="Tool name")
147
+ description: str = Field(description="Tool description")
148
+ args_schema: Optional[Type[BaseModel]] = Field(default=None, description="Pydantic schema for arguments")
149
+
150
+ # SkillLite specific fields
151
+ manager: Any = Field(exclude=True) # SkillManager instance
152
+ skill_name: str = Field(description="SkillLite skill name")
153
+ allow_network: bool = Field(default=False, description="Allow network access")
154
+ timeout: Optional[int] = Field(default=None, description="Execution timeout in seconds")
155
+
156
+ # Security confirmation fields
157
+ sandbox_level: int = Field(default=3, description="Sandbox security level (1/2/3)")
158
+ confirmation_callback: Optional[Any] = Field(
159
+ default=None,
160
+ exclude=True,
161
+ description="Sync callback for security confirmation: (report: str, scan_id: str) -> bool"
162
+ )
163
+ async_confirmation_callback: Optional[Any] = Field(
164
+ default=None,
165
+ exclude=True,
166
+ description="Async callback for security confirmation: (report: str, scan_id: str) -> Future[bool]"
167
+ )
168
+
169
+ model_config = ConfigDict(arbitrary_types_allowed=True)
170
+
171
+ def _run(
172
+ self,
173
+ run_manager: Optional[CallbackManagerForToolRun] = None,
174
+ **kwargs: Any
175
+ ) -> str:
176
+ """
177
+ Execute the skill synchronously using UnifiedExecutionService.
178
+
179
+ This method uses the unified execution layer which:
180
+ 1. Reads sandbox level at runtime
181
+ 2. Handles security scanning and confirmation
182
+ 3. Properly downgrades sandbox level after confirmation
183
+ """
184
+ try:
185
+ # Get skill info
186
+ skill_info = self.manager._registry.get_skill(self.skill_name)
187
+ if not skill_info:
188
+ return f"Error: Skill '{self.skill_name}' not found"
189
+
190
+ # Use UnifiedExecutionService
191
+ from ...sandbox.execution_service import UnifiedExecutionService
192
+
193
+ service = UnifiedExecutionService.get_instance()
194
+ result = service.execute_skill(
195
+ skill_info=skill_info,
196
+ input_data=kwargs,
197
+ confirmation_callback=self.confirmation_callback,
198
+ allow_network=self.allow_network,
199
+ timeout=self.timeout,
200
+ )
201
+
202
+ if result.success:
203
+ return result.output or "Execution completed successfully"
204
+ else:
205
+ return f"Error: {result.error}"
206
+ except Exception as e:
207
+ return f"Execution failed: {str(e)}"
208
+
209
+ async def _arun(
210
+ self,
211
+ run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
212
+ **kwargs: Any
213
+ ) -> str:
214
+ """
215
+ Execute the skill asynchronously using UnifiedExecutionService.
216
+
217
+ This method uses the unified execution layer which:
218
+ 1. Reads sandbox level at runtime
219
+ 2. Handles security scanning and confirmation
220
+ 3. Properly downgrades sandbox level after confirmation
221
+ """
222
+ try:
223
+ # Get skill info
224
+ skill_info = self.manager._registry.get_skill(self.skill_name)
225
+ if not skill_info:
226
+ return f"Error: Skill '{self.skill_name}' not found"
227
+
228
+ # Use UnifiedExecutionService in thread pool
229
+ from ...sandbox.execution_service import UnifiedExecutionService
230
+
231
+ def execute_sync():
232
+ service = UnifiedExecutionService.get_instance()
233
+ # Use async confirmation callback if available, otherwise sync
234
+ callback = self.confirmation_callback
235
+ return service.execute_skill(
236
+ skill_info=skill_info,
237
+ input_data=kwargs,
238
+ confirmation_callback=callback,
239
+ allow_network=self.allow_network,
240
+ timeout=self.timeout,
241
+ )
242
+
243
+ result = await asyncio.to_thread(execute_sync)
244
+
245
+ if result.success:
246
+ return result.output or "Execution completed successfully"
247
+ else:
248
+ return f"Error: {result.error}"
249
+ except Exception as e:
250
+ return f"Execution failed: {str(e)}"
251
+
252
+
253
+ class SkillLiteToolkit:
254
+ """
255
+ LangChain Toolkit for SkillLite.
256
+
257
+ Provides a convenient way to create LangChain tools from all skills
258
+ registered in a SkillManager.
259
+
260
+ Usage:
261
+ manager = SkillManager(skills_dir="./skills")
262
+ tools = SkillLiteToolkit.from_manager(manager)
263
+
264
+ # Or with options
265
+ tools = SkillLiteToolkit.from_manager(
266
+ manager,
267
+ skill_names=["calculator", "web_search"], # Only specific skills
268
+ allow_network=True,
269
+ timeout=60
270
+ )
271
+
272
+ # With security confirmation callback (for sandbox level 3)
273
+ def confirm_execution(report: str, scan_id: str) -> bool:
274
+ print(report)
275
+ return input("Continue? [y/N]: ").lower() == 'y'
276
+
277
+ tools = SkillLiteToolkit.from_manager(
278
+ manager,
279
+ sandbox_level=3,
280
+ confirmation_callback=confirm_execution
281
+ )
282
+ """
283
+
284
+ @staticmethod
285
+ def from_manager(
286
+ manager: "SkillManager",
287
+ skill_names: Optional[List[str]] = None,
288
+ allow_network: bool = False,
289
+ timeout: Optional[int] = None,
290
+ sandbox_level: int = 3,
291
+ confirmation_callback: Optional[ConfirmationCallback] = None,
292
+ async_confirmation_callback: Optional[AsyncConfirmationCallback] = None,
293
+ ) -> List[SkillLiteTool]:
294
+ """
295
+ Create LangChain tools from a SkillManager.
296
+
297
+ Args:
298
+ manager: SkillManager instance with registered skills
299
+ skill_names: Optional list of skill names to include (default: all)
300
+ allow_network: Whether to allow network access for all tools
301
+ timeout: Execution timeout in seconds for all tools
302
+ sandbox_level: Sandbox security level (1/2/3, default: 3)
303
+ - Level 1: No sandbox - direct execution
304
+ - Level 2: Sandbox isolation only
305
+ - Level 3: Sandbox isolation + security scanning (requires confirmation for high-severity issues)
306
+ confirmation_callback: Sync callback for security confirmation.
307
+ Signature: (security_report: str, scan_id: str) -> bool
308
+ Return True to proceed, False to cancel.
309
+ async_confirmation_callback: Async callback for security confirmation.
310
+ Signature: (security_report: str, scan_id: str) -> Future[bool]
311
+ Return True to proceed, False to cancel.
312
+
313
+ Returns:
314
+ List of SkillLiteTool instances
315
+
316
+ Example with confirmation callback:
317
+ def my_callback(report: str, scan_id: str) -> bool:
318
+ print(f"Security Report:\\n{report}")
319
+ response = input("Proceed with execution? [y/N]: ")
320
+ return response.lower() == 'y'
321
+
322
+ tools = SkillLiteToolkit.from_manager(
323
+ manager,
324
+ sandbox_level=3,
325
+ confirmation_callback=my_callback
326
+ )
327
+ """
328
+ tools = []
329
+
330
+ # Get executable skills
331
+ skills = manager.list_executable_skills()
332
+
333
+ for skill in skills:
334
+ # Filter by name if specified
335
+ if skill_names and skill.name not in skill_names:
336
+ continue
337
+
338
+ # Create tool with security confirmation support
339
+ tool = SkillLiteTool(
340
+ name=skill.name,
341
+ description=skill.description or f"Execute the {skill.name} skill",
342
+ manager=manager,
343
+ skill_name=skill.name,
344
+ allow_network=allow_network,
345
+ timeout=timeout,
346
+ sandbox_level=sandbox_level,
347
+ confirmation_callback=confirmation_callback,
348
+ async_confirmation_callback=async_confirmation_callback,
349
+ )
350
+ tools.append(tool)
351
+
352
+ return tools
353
+
354
+
355
+ __all__ = [
356
+ "SkillLiteTool",
357
+ "SkillLiteToolkit",
358
+ "SecurityScanResult",
359
+ "ConfirmationCallback",
360
+ "AsyncConfirmationCallback",
361
+ ]
362
+