skillware 0.3.5__tar.gz → 0.3.7__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.
- {skillware-0.3.5/skillware.egg-info → skillware-0.3.7}/PKG-INFO +22 -7
- {skillware-0.3.5 → skillware-0.3.7}/README.md +12 -5
- {skillware-0.3.5 → skillware-0.3.7}/pyproject.toml +17 -4
- skillware-0.3.7/skills/compliance/mica_module/test_skill.py +67 -0
- skillware-0.3.7/skills/compliance/pii_masker/test_skill.py +58 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/test_skill.py +39 -10
- skillware-0.3.7/skills/data_engineering/synthetic_generator/test_skill.py +58 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/card.json +4 -4
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/instructions.md +5 -5
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/manifest.yaml +1 -1
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/skill.py +5 -0
- skillware-0.3.7/skills/finance/wallet_screening/test_skill.py +84 -0
- skillware-0.3.7/skills/office/pdf_form_filler/test_skill.py +84 -0
- skillware-0.3.7/skills/optimization/prompt_rewriter/__init__.py +3 -0
- skillware-0.3.7/skills/optimization/prompt_rewriter/test_skill.py +55 -0
- skillware-0.3.7/skills/wellness/mental_coach/__init__.py +3 -0
- skillware-0.3.7/skills/wellness/mental_coach/card.json +25 -0
- skillware-0.3.7/skills/wellness/mental_coach/constraints.py +93 -0
- skillware-0.3.7/skills/wellness/mental_coach/crisis_gate.py +175 -0
- skillware-0.3.7/skills/wellness/mental_coach/instructions.md +37 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb/corpus.json +214 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb/crisis_resources.json +219 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb/hard_constraints.yaml +86 -0
- skillware-0.3.7/skills/wellness/mental_coach/kb_provider.py +101 -0
- skillware-0.3.7/skills/wellness/mental_coach/manifest.yaml +66 -0
- skillware-0.3.7/skills/wellness/mental_coach/resources.py +71 -0
- skillware-0.3.7/skills/wellness/mental_coach/skill.py +396 -0
- skillware-0.3.7/skills/wellness/mental_coach/test_skill.py +112 -0
- skillware-0.3.7/skillware/__main__.py +4 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware/cli.py +77 -28
- skillware-0.3.7/skillware/core/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware/version_policy.py +1 -1
- {skillware-0.3.5 → skillware-0.3.7/skillware.egg-info}/PKG-INFO +22 -7
- {skillware-0.3.5 → skillware-0.3.7}/skillware.egg-info/SOURCES.txt +21 -1
- {skillware-0.3.5 → skillware-0.3.7}/skillware.egg-info/requires.txt +11 -1
- {skillware-0.3.5 → skillware-0.3.7}/tests/test_cli.py +60 -0
- skillware-0.3.5/skills/optimization/prompt_rewriter/__init__.py +0 -3
- skillware-0.3.5/tests/test_mica_module.py +0 -66
- {skillware-0.3.5 → skillware-0.3.7}/LICENSE +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/MANIFEST.in +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/setup.cfg +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/mica_module/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/mica_module/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/mica_module/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/mica_module/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/mica_module/mica_corpus.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/mica_module/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/pii_masker/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/pii_masker/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/pii_masker/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/pii_masker/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/pii_masker/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/tos_evaluator/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/tos_evaluator/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/tos_evaluator/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/tos_evaluator/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/tos_evaluator/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/compliance/tos_evaluator/test_skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/novelty_extractor/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/data_engineering/synthetic_generator/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/abis.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/config.yaml.example +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/data/addressbook.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/data/chains.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/data/tokens.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/defi/evm_tx_handler/test_skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/test_skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/dev_tools/issue_resolver/workflow.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/data/entities.ftm.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/data/malicious_scs_2025.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/data/normalized_fbi_lazarus_20250722_180019.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/data/normalized_israel_nbctf_20250722_180019.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/data/normalized_uniswap_trm.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/maintenance/normalization_tool.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/finance/wallet_screening/maintenance/normalize_uniswap_trm.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/pdf_form_filler/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/pdf_form_filler/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/pdf_form_filler/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/pdf_form_filler/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/pdf_form_filler/skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/office/pdf_form_filler/utils.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/optimization/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/optimization/prompt_rewriter/card.json +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/optimization/prompt_rewriter/instructions.md +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/optimization/prompt_rewriter/manifest.yaml +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skills/optimization/prompt_rewriter/skill.py +0 -0
- {skillware-0.3.5/skillware → skillware-0.3.7/skills/wellness}/__init__.py +0 -0
- {skillware-0.3.5/skillware/core → skillware-0.3.7/skillware}/__init__.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware/core/base_skill.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware/core/env.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware/core/loader.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware.egg-info/dependency_links.txt +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware.egg-info/entry_points.txt +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/skillware.egg-info/top_level.txt +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/tests/test_loader.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/tests/test_skill_issuer.py +0 -0
- {skillware-0.3.5 → skillware-0.3.7}/tests/test_version_policy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: skillware
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: A framework for modular, self-contained AI skills.
|
|
5
5
|
Author-email: ARPA Hellenic Logical Systems <skillware-os@arpacorp.net>
|
|
6
6
|
License: MIT License
|
|
@@ -38,13 +38,13 @@ Requires-Dist: pyyaml
|
|
|
38
38
|
Requires-Dist: python-dotenv
|
|
39
39
|
Requires-Dist: beautifulsoup4
|
|
40
40
|
Requires-Dist: packaging
|
|
41
|
+
Requires-Dist: rich>=13.0
|
|
41
42
|
Provides-Extra: dev
|
|
42
43
|
Requires-Dist: pytest; extra == "dev"
|
|
43
44
|
Requires-Dist: pytest-mock; extra == "dev"
|
|
44
45
|
Requires-Dist: flake8; extra == "dev"
|
|
45
46
|
Requires-Dist: black; extra == "dev"
|
|
46
47
|
Provides-Extra: cli
|
|
47
|
-
Requires-Dist: rich>=13.0; extra == "cli"
|
|
48
48
|
Provides-Extra: gemini
|
|
49
49
|
Requires-Dist: google-genai; extra == "gemini"
|
|
50
50
|
Provides-Extra: claude
|
|
@@ -53,12 +53,20 @@ Provides-Extra: openai
|
|
|
53
53
|
Requires-Dist: openai; extra == "openai"
|
|
54
54
|
Provides-Extra: office
|
|
55
55
|
Requires-Dist: pymupdf; extra == "office"
|
|
56
|
+
Provides-Extra: defi
|
|
57
|
+
Requires-Dist: web3>=6.0.0; extra == "defi"
|
|
58
|
+
Provides-Extra: embeddings
|
|
59
|
+
Requires-Dist: fastembed; extra == "embeddings"
|
|
60
|
+
Requires-Dist: numpy; extra == "embeddings"
|
|
56
61
|
Provides-Extra: all
|
|
57
62
|
Requires-Dist: rich>=13.0; extra == "all"
|
|
58
63
|
Requires-Dist: google-genai; extra == "all"
|
|
59
64
|
Requires-Dist: anthropic; extra == "all"
|
|
60
65
|
Requires-Dist: openai; extra == "all"
|
|
61
66
|
Requires-Dist: pymupdf; extra == "all"
|
|
67
|
+
Requires-Dist: web3>=6.0.0; extra == "all"
|
|
68
|
+
Requires-Dist: fastembed; extra == "all"
|
|
69
|
+
Requires-Dist: numpy; extra == "all"
|
|
62
70
|
Dynamic: license-file
|
|
63
71
|
|
|
64
72
|
<div align="center">
|
|
@@ -118,7 +126,7 @@ documentation. Runnable provider scripts are indexed in
|
|
|
118
126
|
```text
|
|
119
127
|
Skillware/
|
|
120
128
|
├── docs/ # Introduction, testing, skill catalog, usage guides (docs/usage/)
|
|
121
|
-
├── examples/ # Provider reference scripts
|
|
129
|
+
├── examples/ # Provider reference scripts — usage demos, not pytest (see examples/README.md)
|
|
122
130
|
├── skills/ # Skill Registry
|
|
123
131
|
│ └── category/ # Domain boundaries (e.g., finance)
|
|
124
132
|
│ └── skill_name/ # The Skill bundle
|
|
@@ -126,7 +134,7 @@ Skillware/
|
|
|
126
134
|
│ ├── skill.py # Executable Python logic
|
|
127
135
|
│ ├── instructions.md # Cognitive map for the LLM
|
|
128
136
|
│ ├── card.json # Optional UI presentation metadata
|
|
129
|
-
│ └── test_skill.py #
|
|
137
|
+
│ └── test_skill.py # Bundle test (required for new skills; see docs/TESTING.md)
|
|
130
138
|
├── skillware/ # Core Framework Package
|
|
131
139
|
│ ├── cli.py # Command-line interface
|
|
132
140
|
│ └── core/
|
|
@@ -135,7 +143,9 @@ Skillware/
|
|
|
135
143
|
│ └── loader.py # Universal Skill Loader and Model Adapter
|
|
136
144
|
├── templates/ # Boilerplate templates for new skills
|
|
137
145
|
│ └── python_skill/ # Standard template with required files
|
|
138
|
-
└── tests/ #
|
|
146
|
+
└── tests/ # Clone-repo tests (framework + optional maintainer skill tests)
|
|
147
|
+
├── test_*.py # Framework tests (loader, CLI, issuer, …)
|
|
148
|
+
└── skills/ # Optional maintainer skill tests (edge cases)
|
|
139
149
|
```
|
|
140
150
|
|
|
141
151
|
## Quick Start
|
|
@@ -161,11 +171,16 @@ pip install -e .
|
|
|
161
171
|
### 2. Verify your installation
|
|
162
172
|
|
|
163
173
|
```bash
|
|
164
|
-
pip install "skillware[cli]"
|
|
165
174
|
skillware list
|
|
166
175
|
```
|
|
167
176
|
|
|
168
|
-
This prints a table of all locally available skills and confirms the install
|
|
177
|
+
This prints a table of all locally available skills and confirms the install
|
|
178
|
+
and path resolution are working. Running `skillware` with no arguments opens
|
|
179
|
+
the interactive menu.
|
|
180
|
+
|
|
181
|
+
If `skillware` is not recognized, Python's `Scripts` directory may not be on
|
|
182
|
+
your PATH — use `python -m skillware list` as a fallback. See
|
|
183
|
+
[CLI Reference](docs/usage/cli.md#running-the-cli) for details.
|
|
169
184
|
|
|
170
185
|
### 3. Configuration
|
|
171
186
|
|
|
@@ -55,7 +55,7 @@ documentation. Runnable provider scripts are indexed in
|
|
|
55
55
|
```text
|
|
56
56
|
Skillware/
|
|
57
57
|
├── docs/ # Introduction, testing, skill catalog, usage guides (docs/usage/)
|
|
58
|
-
├── examples/ # Provider reference scripts
|
|
58
|
+
├── examples/ # Provider reference scripts — usage demos, not pytest (see examples/README.md)
|
|
59
59
|
├── skills/ # Skill Registry
|
|
60
60
|
│ └── category/ # Domain boundaries (e.g., finance)
|
|
61
61
|
│ └── skill_name/ # The Skill bundle
|
|
@@ -63,7 +63,7 @@ Skillware/
|
|
|
63
63
|
│ ├── skill.py # Executable Python logic
|
|
64
64
|
│ ├── instructions.md # Cognitive map for the LLM
|
|
65
65
|
│ ├── card.json # Optional UI presentation metadata
|
|
66
|
-
│ └── test_skill.py #
|
|
66
|
+
│ └── test_skill.py # Bundle test (required for new skills; see docs/TESTING.md)
|
|
67
67
|
├── skillware/ # Core Framework Package
|
|
68
68
|
│ ├── cli.py # Command-line interface
|
|
69
69
|
│ └── core/
|
|
@@ -72,7 +72,9 @@ Skillware/
|
|
|
72
72
|
│ └── loader.py # Universal Skill Loader and Model Adapter
|
|
73
73
|
├── templates/ # Boilerplate templates for new skills
|
|
74
74
|
│ └── python_skill/ # Standard template with required files
|
|
75
|
-
└── tests/ #
|
|
75
|
+
└── tests/ # Clone-repo tests (framework + optional maintainer skill tests)
|
|
76
|
+
├── test_*.py # Framework tests (loader, CLI, issuer, …)
|
|
77
|
+
└── skills/ # Optional maintainer skill tests (edge cases)
|
|
76
78
|
```
|
|
77
79
|
|
|
78
80
|
## Quick Start
|
|
@@ -98,11 +100,16 @@ pip install -e .
|
|
|
98
100
|
### 2. Verify your installation
|
|
99
101
|
|
|
100
102
|
```bash
|
|
101
|
-
pip install "skillware[cli]"
|
|
102
103
|
skillware list
|
|
103
104
|
```
|
|
104
105
|
|
|
105
|
-
This prints a table of all locally available skills and confirms the install
|
|
106
|
+
This prints a table of all locally available skills and confirms the install
|
|
107
|
+
and path resolution are working. Running `skillware` with no arguments opens
|
|
108
|
+
the interactive menu.
|
|
109
|
+
|
|
110
|
+
If `skillware` is not recognized, Python's `Scripts` directory may not be on
|
|
111
|
+
your PATH — use `python -m skillware list` as a fallback. See
|
|
112
|
+
[CLI Reference](docs/usage/cli.md#running-the-cli) for details.
|
|
106
113
|
|
|
107
114
|
### 3. Configuration
|
|
108
115
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "skillware"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.7"
|
|
8
8
|
description = "A framework for modular, self-contained AI skills."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -23,6 +23,7 @@ dependencies = [
|
|
|
23
23
|
"python-dotenv",
|
|
24
24
|
"beautifulsoup4",
|
|
25
25
|
"packaging",
|
|
26
|
+
"rich>=13.0",
|
|
26
27
|
]
|
|
27
28
|
requires-python = ">=3.10"
|
|
28
29
|
|
|
@@ -36,9 +37,7 @@ dev = [
|
|
|
36
37
|
"flake8",
|
|
37
38
|
"black",
|
|
38
39
|
]
|
|
39
|
-
cli = [
|
|
40
|
-
"rich>=13.0",
|
|
41
|
-
]
|
|
40
|
+
cli = []
|
|
42
41
|
gemini = [
|
|
43
42
|
"google-genai",
|
|
44
43
|
]
|
|
@@ -51,12 +50,22 @@ openai = [
|
|
|
51
50
|
office = [
|
|
52
51
|
"pymupdf",
|
|
53
52
|
]
|
|
53
|
+
defi = [
|
|
54
|
+
"web3>=6.0.0",
|
|
55
|
+
]
|
|
56
|
+
embeddings = [
|
|
57
|
+
"fastembed",
|
|
58
|
+
"numpy",
|
|
59
|
+
]
|
|
54
60
|
all = [
|
|
55
61
|
"rich>=13.0",
|
|
56
62
|
"google-genai",
|
|
57
63
|
"anthropic",
|
|
58
64
|
"openai",
|
|
59
65
|
"pymupdf",
|
|
66
|
+
"web3>=6.0.0",
|
|
67
|
+
"fastembed",
|
|
68
|
+
"numpy",
|
|
60
69
|
]
|
|
61
70
|
|
|
62
71
|
[tool.setuptools]
|
|
@@ -70,6 +79,10 @@ include = ["skillware*", "skills*"]
|
|
|
70
79
|
[tool.setuptools.package-data]
|
|
71
80
|
skills = ["**/*"]
|
|
72
81
|
|
|
82
|
+
[tool.pytest.ini_options]
|
|
83
|
+
testpaths = ["tests", "skills"]
|
|
84
|
+
addopts = "--ignore=examples"
|
|
85
|
+
|
|
73
86
|
[tool.black]
|
|
74
87
|
line-length = 88
|
|
75
88
|
target-version = ["py310"]
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from .skill import MiCAModuleSkill
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def skill():
|
|
11
|
+
return MiCAModuleSkill()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def manifest():
|
|
16
|
+
manifest_path = os.path.join(os.path.dirname(__file__), "manifest.yaml")
|
|
17
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
18
|
+
return yaml.safe_load(f)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_skill_manifest_consistency(skill, manifest):
|
|
22
|
+
skill_manifest = skill.manifest
|
|
23
|
+
assert skill_manifest["name"] == manifest["name"]
|
|
24
|
+
assert skill_manifest["version"] == manifest["version"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_stateless_rag_execution(skill):
|
|
28
|
+
result = skill.execute(
|
|
29
|
+
{
|
|
30
|
+
"user_prompt": (
|
|
31
|
+
"I want to issue an asset-referenced token. "
|
|
32
|
+
"What are the authorization rules?"
|
|
33
|
+
),
|
|
34
|
+
"run_evaluator": False,
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
assert result["policy_status"] == "CAUTION"
|
|
38
|
+
assert "retrieved_sections" in result
|
|
39
|
+
assert "final_context_for_agent" in result
|
|
40
|
+
assert "Evaluator disabled" in result["gemini_evaluator_feedback"]["holes_found"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_router_normalization(skill):
|
|
44
|
+
mock_corpus = [
|
|
45
|
+
{
|
|
46
|
+
"title_num": "Title V",
|
|
47
|
+
"article_num": "Article 59",
|
|
48
|
+
"keywords": ["authorisation", "casp"],
|
|
49
|
+
"content": "CASP Authorization rules...",
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
matched = skill._route_and_fetch("Authorization of a CASP", mock_corpus)
|
|
53
|
+
assert len(matched) > 0
|
|
54
|
+
assert matched[0]["article_num"] == "Article 59"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_router_deduplication(skill):
|
|
58
|
+
mock_corpus = [
|
|
59
|
+
{
|
|
60
|
+
"title_num": "Title V",
|
|
61
|
+
"article_num": "Article 59",
|
|
62
|
+
"keywords": ["authorisation", "casp"],
|
|
63
|
+
"content": "CASP Authorization rules...",
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
matched = skill._route_and_fetch("authorisation casp", mock_corpus)
|
|
67
|
+
assert len(matched) == 1
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from .skill import PIIMaskerSkill
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def skill():
|
|
11
|
+
return PIIMaskerSkill()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def manifest():
|
|
16
|
+
manifest_path = os.path.join(os.path.dirname(__file__), "manifest.yaml")
|
|
17
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
18
|
+
return yaml.safe_load(f)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_skill_manifest_consistency(skill, manifest):
|
|
22
|
+
skill_manifest = skill.manifest
|
|
23
|
+
assert skill_manifest["name"] == manifest["name"]
|
|
24
|
+
assert skill_manifest["version"] == manifest["version"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_pii_masker_modes(mocker, skill):
|
|
28
|
+
mock_response = (
|
|
29
|
+
"Hello [PERSON_1], your wallet [CRYPTO_ADDRESS] and [EMAIL] have been verified."
|
|
30
|
+
)
|
|
31
|
+
mocker.patch.object(
|
|
32
|
+
skill,
|
|
33
|
+
"_call_ollama",
|
|
34
|
+
return_value=(mock_response, ["PERSON_1", "CRYPTO_ADDRESS", "EMAIL"]),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
sample_text = (
|
|
38
|
+
"Hello John Doe, your wallet 0xabc and john@doe.com have been verified."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
result_mask = skill.execute({"text": sample_text})
|
|
42
|
+
assert (
|
|
43
|
+
result_mask["sanitized_text"]
|
|
44
|
+
== "Hello [PERSON_1], your wallet [CRYPTO_ADDRESS] and [EMAIL] have been verified."
|
|
45
|
+
)
|
|
46
|
+
assert "PERSON" in result_mask["metadata"]["detected_entities"]
|
|
47
|
+
assert "CRYPTO_ADDRESS" in result_mask["metadata"]["detected_entities"]
|
|
48
|
+
|
|
49
|
+
result_redact = skill.execute({"text": sample_text, "mode": "redact"})
|
|
50
|
+
assert (
|
|
51
|
+
result_redact["sanitized_text"]
|
|
52
|
+
== "Hello XXXX, your wallet XXXX and XXXX have been verified."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
result_remove = skill.execute({"text": sample_text, "mode": "remove"})
|
|
56
|
+
assert (
|
|
57
|
+
result_remove["sanitized_text"] == "Hello , your wallet and have been verified."
|
|
58
|
+
)
|
|
@@ -1,32 +1,66 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from unittest.mock import MagicMock
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
1
5
|
import pytest
|
|
2
6
|
import yaml
|
|
3
|
-
|
|
7
|
+
|
|
4
8
|
from .skill import NoveltyExtractor
|
|
5
9
|
|
|
6
10
|
|
|
11
|
+
def _mock_embed_vectors(texts):
|
|
12
|
+
"""Deterministic keyword vectors — no HuggingFace download in CI."""
|
|
13
|
+
vectors = []
|
|
14
|
+
for text in texts:
|
|
15
|
+
t = text.lower()
|
|
16
|
+
if "bitcoin" in t:
|
|
17
|
+
vectors.append(np.array([1.0, 0.0, 0.0], dtype=float))
|
|
18
|
+
elif "sky" in t or "blue" in t:
|
|
19
|
+
vectors.append(np.array([0.0, 1.0, 0.0], dtype=float))
|
|
20
|
+
elif "python" in t:
|
|
21
|
+
vectors.append(np.array([0.0, 0.0, 1.0], dtype=float))
|
|
22
|
+
else:
|
|
23
|
+
vectors.append(np.array([0.1, 0.1, 0.1], dtype=float))
|
|
24
|
+
return vectors
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture(autouse=True)
|
|
28
|
+
def mock_embedding_model(monkeypatch):
|
|
29
|
+
"""Bundle tests must stay offline; mock fastembed to avoid HF rate limits in CI."""
|
|
30
|
+
NoveltyExtractor._model = None
|
|
31
|
+
mock_model = MagicMock()
|
|
32
|
+
mock_model.embed.side_effect = _mock_embed_vectors
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def _fake_get_model(cls):
|
|
36
|
+
if cls._model is None:
|
|
37
|
+
cls._model = mock_model
|
|
38
|
+
return cls._model
|
|
39
|
+
|
|
40
|
+
monkeypatch.setattr(NoveltyExtractor, "_get_model", _fake_get_model)
|
|
41
|
+
yield
|
|
42
|
+
NoveltyExtractor._model = None
|
|
43
|
+
|
|
44
|
+
|
|
7
45
|
@pytest.fixture
|
|
8
46
|
def skill():
|
|
9
|
-
"""Fixture to initialize the skill."""
|
|
10
47
|
return NoveltyExtractor()
|
|
11
48
|
|
|
12
49
|
|
|
13
50
|
@pytest.fixture
|
|
14
51
|
def manifest():
|
|
15
|
-
"""Fixture to load manifest.yaml for validation."""
|
|
16
52
|
manifest_path = os.path.join(os.path.dirname(__file__), "manifest.yaml")
|
|
17
53
|
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
18
54
|
return yaml.safe_load(f)
|
|
19
55
|
|
|
20
56
|
|
|
21
57
|
def test_skill_manifest_consistency(skill, manifest):
|
|
22
|
-
"""Verify the skill manifest matches manifest.yaml."""
|
|
23
58
|
skill_manifest = skill.manifest
|
|
24
59
|
assert skill_manifest["name"] == manifest["name"]
|
|
25
60
|
assert skill_manifest["version"] == manifest["version"]
|
|
26
61
|
|
|
27
62
|
|
|
28
63
|
def test_skill_returns_dict(skill):
|
|
29
|
-
"""Execution result must be a dictionary."""
|
|
30
64
|
result = skill.execute(
|
|
31
65
|
{
|
|
32
66
|
"dataset_chunk": "Bitcoin is going to rise.\n\nThe sky is blue.",
|
|
@@ -37,7 +71,6 @@ def test_skill_returns_dict(skill):
|
|
|
37
71
|
|
|
38
72
|
|
|
39
73
|
def test_skill_output_keys(skill):
|
|
40
|
-
"""Result must contain the three expected output keys."""
|
|
41
74
|
result = skill.execute(
|
|
42
75
|
{
|
|
43
76
|
"dataset_chunk": "Bitcoin is going to rise.\n\nThe sky is blue.",
|
|
@@ -50,7 +83,6 @@ def test_skill_output_keys(skill):
|
|
|
50
83
|
|
|
51
84
|
|
|
52
85
|
def test_skill_filters_redundant_chunks(skill):
|
|
53
|
-
"""Semantically similar chunks should be filtered out."""
|
|
54
86
|
result = skill.execute(
|
|
55
87
|
{
|
|
56
88
|
"dataset_chunk": (
|
|
@@ -67,7 +99,6 @@ def test_skill_filters_redundant_chunks(skill):
|
|
|
67
99
|
|
|
68
100
|
|
|
69
101
|
def test_skill_empty_input(skill):
|
|
70
|
-
"""Empty input should return empty result without crashing."""
|
|
71
102
|
result = skill.execute(
|
|
72
103
|
{
|
|
73
104
|
"dataset_chunk": "",
|
|
@@ -79,7 +110,6 @@ def test_skill_empty_input(skill):
|
|
|
79
110
|
|
|
80
111
|
|
|
81
112
|
def test_skill_baseline_filters_seen_content(skill):
|
|
82
|
-
"""Chunks already in baseline should be filtered out."""
|
|
83
113
|
baseline = "Bitcoin is going to rise.\n\nThe sky is blue."
|
|
84
114
|
result = skill.execute(
|
|
85
115
|
{
|
|
@@ -93,7 +123,6 @@ def test_skill_baseline_filters_seen_content(skill):
|
|
|
93
123
|
|
|
94
124
|
|
|
95
125
|
def test_skill_sentence_strategy(skill):
|
|
96
|
-
"""Sentence chunking strategy should work without crashing."""
|
|
97
126
|
result = skill.execute(
|
|
98
127
|
{
|
|
99
128
|
"dataset_chunk": "Bitcoin is going to rise. The sky is blue. Python is great.",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from .skill import SyntheticGeneratorSkill
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def skill():
|
|
11
|
+
return SyntheticGeneratorSkill()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def manifest():
|
|
16
|
+
manifest_path = os.path.join(os.path.dirname(__file__), "manifest.yaml")
|
|
17
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
18
|
+
return yaml.safe_load(f)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_skill_manifest_consistency(skill, manifest):
|
|
22
|
+
skill_manifest = skill.manifest
|
|
23
|
+
assert skill_manifest["name"] == manifest["name"]
|
|
24
|
+
assert skill_manifest["version"] == manifest["version"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_entropy_score(skill):
|
|
28
|
+
low_entropy_text = "test " * 100
|
|
29
|
+
score_low = skill._calculate_entropy_score(low_entropy_text)
|
|
30
|
+
|
|
31
|
+
high_text = "The brown fox jumps over the dog. Programming is fun!"
|
|
32
|
+
score_high = skill._calculate_entropy_score(high_text)
|
|
33
|
+
|
|
34
|
+
assert score_high > score_low
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_execute_success(mocker, skill):
|
|
38
|
+
mock_json_response = """```json
|
|
39
|
+
[
|
|
40
|
+
{"instruction": "x", "input": "y", "output": "z"}
|
|
41
|
+
]
|
|
42
|
+
```"""
|
|
43
|
+
mocker.patch.object(skill, "_call_gemini", return_value=mock_json_response)
|
|
44
|
+
|
|
45
|
+
result = skill.execute(
|
|
46
|
+
{
|
|
47
|
+
"domain": "test domain",
|
|
48
|
+
"num_samples": 1,
|
|
49
|
+
"diversity_prompt": "be diverse",
|
|
50
|
+
"model_provider": "gemini",
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
assert result["status"] == "success"
|
|
55
|
+
assert result["provider_used"] == "gemini"
|
|
56
|
+
assert result["samples_generated"] == 1
|
|
57
|
+
assert "samples" in result
|
|
58
|
+
assert result["samples"][0]["instruction"] == "x"
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"ui_schema": {
|
|
13
13
|
"type": "card",
|
|
14
14
|
"fields": [
|
|
15
|
-
{"key": "
|
|
16
|
-
{"key": "summary.
|
|
17
|
-
{"key": "summary.
|
|
18
|
-
{"key": "
|
|
15
|
+
{"key": "metadata.wallet_address", "label": "Address"},
|
|
16
|
+
{"key": "summary.sanctioned_entity_match", "label": "Sanctioned", "type": "status_badge"},
|
|
17
|
+
{"key": "summary.malicious_interaction_count", "label": "Risky Interactions", "type": "count"},
|
|
18
|
+
{"key": "financial_analysis.pnl_eth", "label": "PnL (ETH)", "type": "currency"}
|
|
19
19
|
]
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wallet Screening Skill Instructions
|
|
2
2
|
|
|
3
|
-
You are equipped with the `wallet_screening` skill. This tool allows you to perform due diligence on Ethereum addresses.
|
|
3
|
+
You are equipped with the `finance/wallet_screening` skill. This tool allows you to perform due diligence on Ethereum addresses.
|
|
4
4
|
|
|
5
5
|
## When to use
|
|
6
6
|
Use this skill when the user:
|
|
@@ -13,10 +13,10 @@ Use this skill when the user:
|
|
|
13
13
|
The tool returns a JSON object. You should summarize this for the user in a professional, "Compliance Officer" tone.
|
|
14
14
|
|
|
15
15
|
### Key Fields to Check:
|
|
16
|
-
1. **`summary.
|
|
17
|
-
2. **`summary.
|
|
18
|
-
3. **`
|
|
19
|
-
4. **`
|
|
16
|
+
1. **`summary.sanctioned_entity_match` (Boolean)**: If `true`, this is CRITICAL. Report the `risk_details.sanctions_hits` immediately.
|
|
17
|
+
2. **`summary.malicious_interaction_count` (Integer)**: If > 0, the wallet has touched bad actors. List the `risk_details.malicious_interactions`.
|
|
18
|
+
3. **`financial_analysis.pnl_eth` / `pnl_usd`**: Profit and Loss. Useful for determining if it's a profitable trader or a victim.
|
|
19
|
+
4. **`network_analysis.top_10_counterparties`**: Who are they sending money to?
|
|
20
20
|
|
|
21
21
|
## Safety Protocol
|
|
22
22
|
* If a wallet is **Sanctioned**: severe warning. "⚠️ WARNING: This wallet appears on the following sanctions lists..."
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name: wallet_screening
|
|
1
|
+
name: finance/wallet_screening
|
|
2
2
|
version: 1.0.0
|
|
3
3
|
description: |
|
|
4
4
|
A comprehensive crypto wallet screening tool. Checks Ethereum addresses against sanctions lists (OFAC, FBI, etc.) and known malicious contracts (Mixers, Scams). analyze transaction history for risk.
|
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
import requests
|
|
5
5
|
import glob
|
|
6
|
+
import yaml
|
|
6
7
|
from typing import Any, Dict, List, Optional
|
|
7
8
|
from datetime import datetime
|
|
8
9
|
from skillware.core.base_skill import BaseSkill
|
|
@@ -44,6 +45,10 @@ class WalletScreeningSkill(BaseSkill):
|
|
|
44
45
|
|
|
45
46
|
@property
|
|
46
47
|
def manifest(self) -> Dict[str, Any]:
|
|
48
|
+
manifest_path = os.path.join(os.path.dirname(__file__), "manifest.yaml")
|
|
49
|
+
if os.path.exists(manifest_path):
|
|
50
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
51
|
+
return yaml.safe_load(f)
|
|
47
52
|
return {}
|
|
48
53
|
|
|
49
54
|
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from .skill import WalletScreeningSkill
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def skill():
|
|
12
|
+
return WalletScreeningSkill()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def manifest():
|
|
17
|
+
manifest_path = os.path.join(os.path.dirname(__file__), "manifest.yaml")
|
|
18
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
19
|
+
return yaml.safe_load(f)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_skill_manifest_consistency(skill, manifest):
|
|
23
|
+
skill_manifest = skill.manifest
|
|
24
|
+
assert skill_manifest["name"] == manifest["name"]
|
|
25
|
+
assert skill_manifest["version"] == manifest["version"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_invalid_address(skill):
|
|
29
|
+
result = skill.execute({"address": "invalid_addr"})
|
|
30
|
+
assert "error" in result
|
|
31
|
+
assert "Invalid Ethereum address" in result["error"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_missing_api_key(skill):
|
|
35
|
+
skill.etherscan_api_key = None
|
|
36
|
+
result = skill.execute({"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"})
|
|
37
|
+
assert "error" in result
|
|
38
|
+
assert "Missing ETHERSCAN_API_KEY" in result["error"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@patch("skills.finance.wallet_screening.skill.requests.get")
|
|
42
|
+
def test_execute_success(mock_get, skill):
|
|
43
|
+
skill.etherscan_api_key = "dummy_key"
|
|
44
|
+
|
|
45
|
+
mock_eth_balance = MagicMock()
|
|
46
|
+
mock_eth_balance.json.return_value = {
|
|
47
|
+
"status": "1",
|
|
48
|
+
"result": "1000000000000000000",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
mock_txs = MagicMock()
|
|
52
|
+
mock_txs.json.return_value = {
|
|
53
|
+
"status": "1",
|
|
54
|
+
"result": [
|
|
55
|
+
{
|
|
56
|
+
"from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".lower(),
|
|
57
|
+
"to": "0x123",
|
|
58
|
+
"value": "500000000000000000",
|
|
59
|
+
"isError": "0",
|
|
60
|
+
"gasUsed": "21000",
|
|
61
|
+
"gasPrice": "1000000000",
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
mock_price = MagicMock()
|
|
67
|
+
mock_price.json.return_value = {"ethereum": {"usd": 2000.0, "eur": 1800.0}}
|
|
68
|
+
|
|
69
|
+
def get_side_effect(url, **kwargs):
|
|
70
|
+
params = kwargs.get("params") or {}
|
|
71
|
+
if params.get("action") == "balance":
|
|
72
|
+
return mock_eth_balance
|
|
73
|
+
if params.get("action") == "txlist":
|
|
74
|
+
return mock_txs
|
|
75
|
+
return mock_price
|
|
76
|
+
|
|
77
|
+
mock_get.side_effect = get_side_effect
|
|
78
|
+
|
|
79
|
+
result = skill.execute({"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"})
|
|
80
|
+
|
|
81
|
+
assert "error" not in result
|
|
82
|
+
assert "summary" in result
|
|
83
|
+
assert result["summary"]["balance_eth"] == 1.0
|
|
84
|
+
assert result["summary"]["balance_usd"] == 2000.0
|