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.
- {skilllite-0.1.1/skilllite.egg-info → skilllite-0.1.2}/PKG-INFO +98 -1
- {skilllite-0.1.1 → skilllite-0.1.2}/README.md +91 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/pyproject.toml +5 -1
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/__init__.py +2 -0
- skilllite-0.1.2/skilllite/core/adapters/__init__.py +74 -0
- skilllite-0.1.2/skilllite/core/adapters/langchain.py +362 -0
- skilllite-0.1.2/skilllite/core/adapters/llamaindex.py +264 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/handler.py +179 -4
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/loops.py +175 -13
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/manager.py +82 -15
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/metadata.py +14 -7
- skilllite-0.1.2/skilllite/core/security.py +420 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/mcp/server.py +266 -58
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/quick.py +14 -4
- skilllite-0.1.2/skilllite/sandbox/context.py +155 -0
- skilllite-0.1.2/skilllite/sandbox/execution_service.py +254 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/skillbox/executor.py +124 -19
- skilllite-0.1.2/skilllite/sandbox/unified_executor.py +359 -0
- {skilllite-0.1.1 → skilllite-0.1.2/skilllite.egg-info}/PKG-INFO +98 -1
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/SOURCES.txt +7 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/requires.txt +8 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/LICENSE +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/MANIFEST.in +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/setup.cfg +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/setup.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/__init__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/analyzer.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/builtin_tools.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/__init__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/__main__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/binary.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/integrations/__init__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/integrations/opencode.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/main.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/cli/mcp.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/executor.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/prompt_builder.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/registry.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/skill_info.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/tool_builder.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/core/tools.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/mcp/__init__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/__init__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/base.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/config.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/skillbox/__init__.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/skillbox/binary.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/sandbox/utils.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite/validation.py +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/dependency_links.txt +0 -0
- {skilllite-0.1.1 → skilllite-0.1.2}/skilllite.egg-info/entry_points.txt +0 -0
- {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.
|
|
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.
|
|
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",
|
|
@@ -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
|
+
|