cognitive-modules 0.1.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- cognitive/loader.py +146 -21
- cognitive/runner.py +155 -19
- cognitive/templates.py +1 -1
- {cognitive_modules-0.1.1.dist-info → cognitive_modules-0.3.0.dist-info}/METADATA +131 -2
- cognitive_modules-0.3.0.dist-info/RECORD +15 -0
- cognitive_modules-0.1.1.dist-info/RECORD +0 -15
- {cognitive_modules-0.1.1.dist-info → cognitive_modules-0.3.0.dist-info}/WHEEL +0 -0
- {cognitive_modules-0.1.1.dist-info → cognitive_modules-0.3.0.dist-info}/entry_points.txt +0 -0
- {cognitive_modules-0.1.1.dist-info → cognitive_modules-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {cognitive_modules-0.1.1.dist-info → cognitive_modules-0.3.0.dist-info}/top_level.txt +0 -0
cognitive/loader.py
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module Loader - Load cognitive modules in
|
|
2
|
+
Module Loader - Load cognitive modules in all formats.
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Format v2 (recommended):
|
|
5
|
+
- module.yaml (machine-readable manifest)
|
|
6
|
+
- prompt.md (human-readable prompt)
|
|
7
|
+
- schema.json (input + output + error)
|
|
8
|
+
- tests/ (golden tests)
|
|
9
|
+
|
|
10
|
+
Format v1 (legacy, still supported):
|
|
11
|
+
- MODULE.md (YAML frontmatter + prompt)
|
|
12
|
+
- schema.json (input + output)
|
|
13
|
+
|
|
14
|
+
Format v0 (old, deprecated):
|
|
5
15
|
- module.md (YAML frontmatter)
|
|
6
16
|
- input.schema.json
|
|
7
17
|
- output.schema.json
|
|
8
18
|
- constraints.yaml
|
|
9
19
|
- prompt.txt
|
|
10
|
-
- examples/
|
|
11
|
-
|
|
12
|
-
New format (2 files):
|
|
13
|
-
- MODULE.md (YAML frontmatter + prompt)
|
|
14
|
-
- schema.json (input + output combined)
|
|
15
20
|
"""
|
|
16
21
|
|
|
17
22
|
import json
|
|
@@ -22,13 +27,15 @@ import yaml
|
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
def detect_format(module_path: Path) -> str:
|
|
25
|
-
"""Detect module format: '
|
|
26
|
-
if (module_path / "
|
|
27
|
-
return "
|
|
30
|
+
"""Detect module format: 'v2', 'v1', or 'v0'."""
|
|
31
|
+
if (module_path / "module.yaml").exists():
|
|
32
|
+
return "v2"
|
|
33
|
+
elif (module_path / "MODULE.md").exists():
|
|
34
|
+
return "v1"
|
|
28
35
|
elif (module_path / "module.md").exists():
|
|
29
|
-
return "
|
|
36
|
+
return "v0"
|
|
30
37
|
else:
|
|
31
|
-
raise FileNotFoundError(f"No MODULE.md or module.md found in {module_path}")
|
|
38
|
+
raise FileNotFoundError(f"No module.yaml, MODULE.md, or module.md found in {module_path}")
|
|
32
39
|
|
|
33
40
|
|
|
34
41
|
def parse_frontmatter(content: str) -> tuple[dict, str]:
|
|
@@ -45,8 +52,90 @@ def parse_frontmatter(content: str) -> tuple[dict, str]:
|
|
|
45
52
|
return frontmatter, body
|
|
46
53
|
|
|
47
54
|
|
|
48
|
-
def
|
|
49
|
-
"""Load module in
|
|
55
|
+
def load_v2_format(module_path: Path) -> dict:
|
|
56
|
+
"""Load module in v2 format (module.yaml + prompt.md + schema.json)."""
|
|
57
|
+
# Load module.yaml
|
|
58
|
+
with open(module_path / "module.yaml", 'r', encoding='utf-8') as f:
|
|
59
|
+
manifest = yaml.safe_load(f)
|
|
60
|
+
|
|
61
|
+
# Load prompt.md
|
|
62
|
+
prompt_path = module_path / "prompt.md"
|
|
63
|
+
if prompt_path.exists():
|
|
64
|
+
with open(prompt_path, 'r', encoding='utf-8') as f:
|
|
65
|
+
prompt = f.read()
|
|
66
|
+
else:
|
|
67
|
+
prompt = ""
|
|
68
|
+
|
|
69
|
+
# Load schema.json
|
|
70
|
+
schema_path = module_path / "schema.json"
|
|
71
|
+
if schema_path.exists():
|
|
72
|
+
with open(schema_path, 'r', encoding='utf-8') as f:
|
|
73
|
+
schema = json.load(f)
|
|
74
|
+
input_schema = schema.get("input", {})
|
|
75
|
+
output_schema = schema.get("output", {})
|
|
76
|
+
error_schema = schema.get("error", {})
|
|
77
|
+
else:
|
|
78
|
+
input_schema = {}
|
|
79
|
+
output_schema = {}
|
|
80
|
+
error_schema = {}
|
|
81
|
+
|
|
82
|
+
# Extract constraints (supports both old and new format)
|
|
83
|
+
constraints_raw = manifest.get("constraints", {})
|
|
84
|
+
policies_raw = manifest.get("policies", {})
|
|
85
|
+
|
|
86
|
+
constraints = {
|
|
87
|
+
"operational": {
|
|
88
|
+
"no_external_network": constraints_raw.get("no_network", True) or policies_raw.get("network") == "deny",
|
|
89
|
+
"no_side_effects": constraints_raw.get("no_side_effects", True) or policies_raw.get("side_effects") == "deny",
|
|
90
|
+
"no_file_write": constraints_raw.get("no_file_write", True) or policies_raw.get("filesystem_write") == "deny",
|
|
91
|
+
"no_inventing_data": constraints_raw.get("no_inventing_data", True),
|
|
92
|
+
},
|
|
93
|
+
"output_quality": {
|
|
94
|
+
"require_confidence": manifest.get("output", {}).get("require_confidence", True),
|
|
95
|
+
"require_rationale": manifest.get("output", {}).get("require_rationale", True),
|
|
96
|
+
"require_behavior_equivalence": manifest.get("output", {}).get("require_behavior_equivalence", False),
|
|
97
|
+
},
|
|
98
|
+
"behavior_equivalence_false_max_confidence": constraints_raw.get("behavior_equivalence_false_max_confidence", 0.7),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Extract policies (v2.1)
|
|
102
|
+
policies = manifest.get("policies", {})
|
|
103
|
+
|
|
104
|
+
# Extract tools policy
|
|
105
|
+
tools = manifest.get("tools", {})
|
|
106
|
+
|
|
107
|
+
# Extract output contract
|
|
108
|
+
output_contract = manifest.get("output", {})
|
|
109
|
+
|
|
110
|
+
# Extract failure contract
|
|
111
|
+
failure_contract = manifest.get("failure", {})
|
|
112
|
+
|
|
113
|
+
# Extract runtime requirements
|
|
114
|
+
runtime_requirements = manifest.get("runtime_requirements", {})
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
"name": manifest.get("name", module_path.name),
|
|
118
|
+
"version": manifest.get("version", "1.0.0"),
|
|
119
|
+
"responsibility": manifest.get("responsibility", ""),
|
|
120
|
+
"excludes": manifest.get("excludes", []),
|
|
121
|
+
"path": module_path,
|
|
122
|
+
"format": "v2",
|
|
123
|
+
"metadata": manifest,
|
|
124
|
+
"input_schema": input_schema,
|
|
125
|
+
"output_schema": output_schema,
|
|
126
|
+
"error_schema": error_schema,
|
|
127
|
+
"constraints": constraints,
|
|
128
|
+
"policies": policies,
|
|
129
|
+
"tools": tools,
|
|
130
|
+
"output_contract": output_contract,
|
|
131
|
+
"failure_contract": failure_contract,
|
|
132
|
+
"runtime_requirements": runtime_requirements,
|
|
133
|
+
"prompt": prompt,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def load_v1_format(module_path: Path) -> dict:
|
|
138
|
+
"""Load module in v1 format (MODULE.md + schema.json)."""
|
|
50
139
|
# Load MODULE.md
|
|
51
140
|
with open(module_path / "MODULE.md", 'r', encoding='utf-8') as f:
|
|
52
141
|
content = f.read()
|
|
@@ -79,8 +168,11 @@ def load_new_format(module_path: Path) -> dict:
|
|
|
79
168
|
|
|
80
169
|
return {
|
|
81
170
|
"name": metadata.get("name", module_path.name),
|
|
171
|
+
"version": metadata.get("version", "1.0.0"),
|
|
172
|
+
"responsibility": metadata.get("responsibility", ""),
|
|
173
|
+
"excludes": metadata.get("excludes", []),
|
|
82
174
|
"path": module_path,
|
|
83
|
-
"format": "
|
|
175
|
+
"format": "v1",
|
|
84
176
|
"metadata": metadata,
|
|
85
177
|
"input_schema": input_schema,
|
|
86
178
|
"output_schema": output_schema,
|
|
@@ -89,8 +181,8 @@ def load_new_format(module_path: Path) -> dict:
|
|
|
89
181
|
}
|
|
90
182
|
|
|
91
183
|
|
|
92
|
-
def
|
|
93
|
-
"""Load module in
|
|
184
|
+
def load_v0_format(module_path: Path) -> dict:
|
|
185
|
+
"""Load module in v0 format (old 6-file format)."""
|
|
94
186
|
# Load module.md
|
|
95
187
|
with open(module_path / "module.md", 'r', encoding='utf-8') as f:
|
|
96
188
|
content = f.read()
|
|
@@ -114,8 +206,11 @@ def load_old_format(module_path: Path) -> dict:
|
|
|
114
206
|
|
|
115
207
|
return {
|
|
116
208
|
"name": metadata.get("name", module_path.name),
|
|
209
|
+
"version": metadata.get("version", "1.0.0"),
|
|
210
|
+
"responsibility": metadata.get("responsibility", ""),
|
|
211
|
+
"excludes": [],
|
|
117
212
|
"path": module_path,
|
|
118
|
-
"format": "
|
|
213
|
+
"format": "v0",
|
|
119
214
|
"metadata": metadata,
|
|
120
215
|
"input_schema": input_schema,
|
|
121
216
|
"output_schema": output_schema,
|
|
@@ -127,7 +222,37 @@ def load_old_format(module_path: Path) -> dict:
|
|
|
127
222
|
def load_module(module_path: Path) -> dict:
|
|
128
223
|
"""Load a module, auto-detecting format."""
|
|
129
224
|
fmt = detect_format(module_path)
|
|
130
|
-
if fmt == "
|
|
131
|
-
return
|
|
225
|
+
if fmt == "v2":
|
|
226
|
+
return load_v2_format(module_path)
|
|
227
|
+
elif fmt == "v1":
|
|
228
|
+
return load_v1_format(module_path)
|
|
132
229
|
else:
|
|
133
|
-
return
|
|
230
|
+
return load_v0_format(module_path)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def find_module(name: str, search_paths: list[Path]) -> Optional[dict]:
|
|
234
|
+
"""Find and load a module by name from search paths."""
|
|
235
|
+
for base_path in search_paths:
|
|
236
|
+
module_path = base_path / name
|
|
237
|
+
if module_path.exists():
|
|
238
|
+
try:
|
|
239
|
+
return load_module(module_path)
|
|
240
|
+
except FileNotFoundError:
|
|
241
|
+
continue
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def list_modules(search_paths: list[Path]) -> list[dict]:
|
|
246
|
+
"""List all modules in search paths."""
|
|
247
|
+
modules = []
|
|
248
|
+
for base_path in search_paths:
|
|
249
|
+
if not base_path.exists():
|
|
250
|
+
continue
|
|
251
|
+
for module_dir in base_path.iterdir():
|
|
252
|
+
if module_dir.is_dir():
|
|
253
|
+
try:
|
|
254
|
+
module = load_module(module_dir)
|
|
255
|
+
modules.append(module)
|
|
256
|
+
except FileNotFoundError:
|
|
257
|
+
continue
|
|
258
|
+
return modules
|
cognitive/runner.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Module Runner - Execute cognitive modules with validation.
|
|
3
|
-
Supports
|
|
3
|
+
Supports v2 envelope format and legacy formats.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import json
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
8
|
+
from typing import Optional, TypedDict, Union
|
|
9
9
|
|
|
10
10
|
import jsonschema
|
|
11
11
|
import yaml
|
|
@@ -15,6 +15,25 @@ from .loader import load_module
|
|
|
15
15
|
from .providers import call_llm
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
class EnvelopeError(TypedDict):
|
|
19
|
+
code: str
|
|
20
|
+
message: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class EnvelopeSuccess(TypedDict):
|
|
24
|
+
ok: bool # True
|
|
25
|
+
data: dict
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EnvelopeFailure(TypedDict):
|
|
29
|
+
ok: bool # False
|
|
30
|
+
error: EnvelopeError
|
|
31
|
+
partial_data: Optional[dict]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
EnvelopeResponse = Union[EnvelopeSuccess, EnvelopeFailure]
|
|
35
|
+
|
|
36
|
+
|
|
18
37
|
def validate_data(data: dict, schema: dict, label: str = "Data") -> list[str]:
|
|
19
38
|
"""Validate data against schema. Returns list of errors."""
|
|
20
39
|
errors = []
|
|
@@ -32,7 +51,7 @@ def validate_data(data: dict, schema: dict, label: str = "Data") -> list[str]:
|
|
|
32
51
|
def substitute_arguments(text: str, input_data: dict) -> str:
|
|
33
52
|
"""Substitute $ARGUMENTS and $N placeholders in text."""
|
|
34
53
|
# Get arguments
|
|
35
|
-
args_value = input_data.get("$ARGUMENTS", input_data.get("query", ""))
|
|
54
|
+
args_value = input_data.get("$ARGUMENTS", input_data.get("query", input_data.get("code", "")))
|
|
36
55
|
|
|
37
56
|
# Replace $ARGUMENTS
|
|
38
57
|
text = text.replace("$ARGUMENTS", str(args_value))
|
|
@@ -47,7 +66,7 @@ def substitute_arguments(text: str, input_data: dict) -> str:
|
|
|
47
66
|
return text
|
|
48
67
|
|
|
49
68
|
|
|
50
|
-
def build_prompt(module: dict, input_data: dict) -> str:
|
|
69
|
+
def build_prompt(module: dict, input_data: dict, use_envelope: bool = False) -> str:
|
|
51
70
|
"""Build the complete prompt for the LLM."""
|
|
52
71
|
# Substitute $ARGUMENTS in prompt
|
|
53
72
|
prompt = substitute_arguments(module["prompt"], input_data)
|
|
@@ -60,10 +79,23 @@ def build_prompt(module: dict, input_data: dict) -> str:
|
|
|
60
79
|
"```json\n",
|
|
61
80
|
json.dumps(input_data, indent=2, ensure_ascii=False),
|
|
62
81
|
"\n```\n",
|
|
63
|
-
"\n## Instructions\n",
|
|
64
|
-
"Analyze the input and generate output matching the required schema.",
|
|
65
|
-
"Return ONLY valid JSON. Do not include any text before or after the JSON.",
|
|
66
82
|
]
|
|
83
|
+
|
|
84
|
+
if use_envelope:
|
|
85
|
+
parts.extend([
|
|
86
|
+
"\n## Response Format (Envelope)\n",
|
|
87
|
+
"You MUST wrap your response in the envelope format:\n",
|
|
88
|
+
"- Success: { \"ok\": true, \"data\": { ...your output... } }\n",
|
|
89
|
+
"- Error: { \"ok\": false, \"error\": { \"code\": \"ERROR_CODE\", \"message\": \"...\" } }\n",
|
|
90
|
+
"Return ONLY valid JSON.\n",
|
|
91
|
+
])
|
|
92
|
+
else:
|
|
93
|
+
parts.extend([
|
|
94
|
+
"\n## Instructions\n",
|
|
95
|
+
"Analyze the input and generate output matching the required schema.",
|
|
96
|
+
"Return ONLY valid JSON. Do not include any text before or after the JSON.",
|
|
97
|
+
])
|
|
98
|
+
|
|
67
99
|
return "".join(parts)
|
|
68
100
|
|
|
69
101
|
|
|
@@ -85,16 +117,56 @@ def parse_llm_response(response: str) -> dict:
|
|
|
85
117
|
return json.loads(text)
|
|
86
118
|
|
|
87
119
|
|
|
120
|
+
def is_envelope_response(data: dict) -> bool:
|
|
121
|
+
"""Check if response is in envelope format."""
|
|
122
|
+
return isinstance(data.get("ok"), bool)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def parse_envelope_response(data: dict) -> EnvelopeResponse:
|
|
126
|
+
"""Parse and normalize envelope response."""
|
|
127
|
+
if data.get("ok") is True:
|
|
128
|
+
return {
|
|
129
|
+
"ok": True,
|
|
130
|
+
"data": data.get("data", {})
|
|
131
|
+
}
|
|
132
|
+
else:
|
|
133
|
+
return {
|
|
134
|
+
"ok": False,
|
|
135
|
+
"error": data.get("error", {"code": "UNKNOWN", "message": "Unknown error"}),
|
|
136
|
+
"partial_data": data.get("partial_data")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def convert_to_envelope(data: dict, is_error: bool = False) -> EnvelopeResponse:
|
|
141
|
+
"""Convert legacy format to envelope format."""
|
|
142
|
+
if is_error or "error" in data:
|
|
143
|
+
error = data.get("error", {})
|
|
144
|
+
return {
|
|
145
|
+
"ok": False,
|
|
146
|
+
"error": {
|
|
147
|
+
"code": error.get("code", "UNKNOWN"),
|
|
148
|
+
"message": error.get("message", str(error))
|
|
149
|
+
},
|
|
150
|
+
"partial_data": None
|
|
151
|
+
}
|
|
152
|
+
else:
|
|
153
|
+
return {
|
|
154
|
+
"ok": True,
|
|
155
|
+
"data": data
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
88
159
|
def run_module(
|
|
89
160
|
name_or_path: str,
|
|
90
161
|
input_data: dict,
|
|
91
162
|
validate_input: bool = True,
|
|
92
163
|
validate_output: bool = True,
|
|
93
164
|
model: Optional[str] = None,
|
|
94
|
-
|
|
165
|
+
use_envelope: Optional[bool] = None,
|
|
166
|
+
) -> EnvelopeResponse:
|
|
95
167
|
"""
|
|
96
168
|
Run a cognitive module with the given input.
|
|
97
|
-
|
|
169
|
+
Returns envelope format response.
|
|
98
170
|
|
|
99
171
|
Args:
|
|
100
172
|
name_or_path: Module name or path to module directory
|
|
@@ -102,9 +174,10 @@ def run_module(
|
|
|
102
174
|
validate_input: Whether to validate input against schema
|
|
103
175
|
validate_output: Whether to validate output against schema
|
|
104
176
|
model: Optional model override
|
|
177
|
+
use_envelope: Force envelope format (auto-detect if None)
|
|
105
178
|
|
|
106
179
|
Returns:
|
|
107
|
-
|
|
180
|
+
EnvelopeResponse with ok=True/False and data/error
|
|
108
181
|
"""
|
|
109
182
|
# Find module path
|
|
110
183
|
path = Path(name_or_path)
|
|
@@ -113,28 +186,91 @@ def run_module(
|
|
|
113
186
|
else:
|
|
114
187
|
module_path = find_module(name_or_path)
|
|
115
188
|
if not module_path:
|
|
116
|
-
|
|
189
|
+
return {
|
|
190
|
+
"ok": False,
|
|
191
|
+
"error": {"code": "MODULE_NOT_FOUND", "message": f"Module not found: {name_or_path}"},
|
|
192
|
+
"partial_data": None
|
|
193
|
+
}
|
|
117
194
|
|
|
118
195
|
# Load module (auto-detects format)
|
|
119
196
|
module = load_module(module_path)
|
|
120
197
|
|
|
198
|
+
# Determine if we should use envelope format
|
|
199
|
+
should_use_envelope = use_envelope
|
|
200
|
+
if should_use_envelope is None:
|
|
201
|
+
# Auto-detect: use envelope for v2 format or if output.envelope is True
|
|
202
|
+
output_contract = module.get("output_contract", {})
|
|
203
|
+
should_use_envelope = (
|
|
204
|
+
module.get("format") == "v2" or
|
|
205
|
+
output_contract.get("envelope", False)
|
|
206
|
+
)
|
|
207
|
+
|
|
121
208
|
# Validate input
|
|
122
209
|
if validate_input and module["input_schema"]:
|
|
123
210
|
errors = validate_data(input_data, module["input_schema"], "Input")
|
|
124
211
|
if errors:
|
|
125
|
-
|
|
212
|
+
return {
|
|
213
|
+
"ok": False,
|
|
214
|
+
"error": {"code": "INVALID_INPUT", "message": str(errors)},
|
|
215
|
+
"partial_data": None
|
|
216
|
+
}
|
|
126
217
|
|
|
127
218
|
# Build prompt and call LLM
|
|
128
|
-
full_prompt = build_prompt(module, input_data)
|
|
219
|
+
full_prompt = build_prompt(module, input_data, use_envelope=should_use_envelope)
|
|
129
220
|
response = call_llm(full_prompt, model=model)
|
|
130
221
|
|
|
131
222
|
# Parse response
|
|
132
|
-
|
|
223
|
+
try:
|
|
224
|
+
output_data = parse_llm_response(response)
|
|
225
|
+
except json.JSONDecodeError as e:
|
|
226
|
+
return {
|
|
227
|
+
"ok": False,
|
|
228
|
+
"error": {"code": "PARSE_ERROR", "message": f"Failed to parse JSON: {e}"},
|
|
229
|
+
"partial_data": None
|
|
230
|
+
}
|
|
133
231
|
|
|
134
|
-
#
|
|
135
|
-
if
|
|
136
|
-
|
|
232
|
+
# Handle envelope format
|
|
233
|
+
if is_envelope_response(output_data):
|
|
234
|
+
result = parse_envelope_response(output_data)
|
|
235
|
+
else:
|
|
236
|
+
# Convert legacy format to envelope
|
|
237
|
+
result = convert_to_envelope(output_data)
|
|
238
|
+
|
|
239
|
+
# Validate output (only for success responses)
|
|
240
|
+
if result["ok"] and validate_output and module["output_schema"]:
|
|
241
|
+
data_to_validate = result.get("data", {})
|
|
242
|
+
errors = validate_data(data_to_validate, module["output_schema"], "Output")
|
|
137
243
|
if errors:
|
|
138
|
-
|
|
244
|
+
return {
|
|
245
|
+
"ok": False,
|
|
246
|
+
"error": {"code": "OUTPUT_VALIDATION_ERROR", "message": str(errors)},
|
|
247
|
+
"partial_data": data_to_validate
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return result
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def run_module_legacy(
|
|
254
|
+
name_or_path: str,
|
|
255
|
+
input_data: dict,
|
|
256
|
+
validate_input: bool = True,
|
|
257
|
+
validate_output: bool = True,
|
|
258
|
+
model: Optional[str] = None,
|
|
259
|
+
) -> dict:
|
|
260
|
+
"""
|
|
261
|
+
Run a cognitive module (legacy API, returns raw output).
|
|
262
|
+
For backward compatibility.
|
|
263
|
+
"""
|
|
264
|
+
result = run_module(
|
|
265
|
+
name_or_path,
|
|
266
|
+
input_data,
|
|
267
|
+
validate_input=validate_input,
|
|
268
|
+
validate_output=validate_output,
|
|
269
|
+
model=model,
|
|
270
|
+
use_envelope=False
|
|
271
|
+
)
|
|
139
272
|
|
|
140
|
-
|
|
273
|
+
if result["ok"]:
|
|
274
|
+
return result["data"]
|
|
275
|
+
else:
|
|
276
|
+
raise ValueError(f"{result['error']['code']}: {result['error']['message']}")
|
cognitive/templates.py
CHANGED
|
@@ -83,7 +83,7 @@ EXAMPLE_OUTPUT = {
|
|
|
83
83
|
def get_schema_template(name: str) -> dict:
|
|
84
84
|
"""Generate schema template as dict."""
|
|
85
85
|
return {
|
|
86
|
-
"$schema": "https://
|
|
86
|
+
"$schema": "https://ziel-io.github.io/cognitive-modules/schema/v1.json",
|
|
87
87
|
"$id": name,
|
|
88
88
|
"title": f"{name.replace('-', ' ').title()} Schema",
|
|
89
89
|
"input": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognitive-modules
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Structured LLM task runner with schema validation, confidence scoring, and subagent orchestration
|
|
5
5
|
Author: ziel-io
|
|
6
6
|
License: MIT
|
|
@@ -151,6 +151,7 @@ cog doctor
|
|
|
151
151
|
| 模块 | 功能 | 示例 |
|
|
152
152
|
|------|------|------|
|
|
153
153
|
| `code-reviewer` | 代码审查 | `cog run code-reviewer --args "你的代码"` |
|
|
154
|
+
| `code-simplifier` | 代码简化 | `cog run code-simplifier --args "复杂代码"` |
|
|
154
155
|
| `task-prioritizer` | 任务优先级排序 | `cog run task-prioritizer --args "任务1,任务2"` |
|
|
155
156
|
| `api-designer` | REST API 设计 | `cog run api-designer --args "订单系统"` |
|
|
156
157
|
| `ui-spec-generator` | UI 规范生成 | `cog run ui-spec-generator --args "电商首页"` |
|
|
@@ -241,6 +242,134 @@ export LLM_PROVIDER=ollama
|
|
|
241
242
|
cog doctor
|
|
242
243
|
```
|
|
243
244
|
|
|
245
|
+
## 创建新模块(完整流程)
|
|
246
|
+
|
|
247
|
+
以 `code-simplifier` 为例:
|
|
248
|
+
|
|
249
|
+
### Step 1: 创建目录结构
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
mkdir -p cognitive/modules/code-simplifier
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Step 2: 编写 MODULE.md
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
cat > cognitive/modules/code-simplifier/MODULE.md << 'EOF'
|
|
259
|
+
---
|
|
260
|
+
name: code-simplifier
|
|
261
|
+
version: 1.0.0
|
|
262
|
+
responsibility: Simplify complex code while preserving functionality
|
|
263
|
+
|
|
264
|
+
excludes:
|
|
265
|
+
- Changing the code's behavior
|
|
266
|
+
- Adding new features
|
|
267
|
+
- Removing functionality
|
|
268
|
+
|
|
269
|
+
constraints:
|
|
270
|
+
no_network: true
|
|
271
|
+
no_side_effects: true
|
|
272
|
+
require_confidence: true
|
|
273
|
+
require_rationale: true
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
# Code Simplifier Module
|
|
277
|
+
|
|
278
|
+
You are an expert at refactoring and simplifying code.
|
|
279
|
+
|
|
280
|
+
## Input
|
|
281
|
+
|
|
282
|
+
Code to simplify: $ARGUMENTS
|
|
283
|
+
|
|
284
|
+
## Simplification Strategies
|
|
285
|
+
|
|
286
|
+
1. **Remove redundancy** - Eliminate duplicate code
|
|
287
|
+
2. **Improve naming** - Use clear, descriptive names
|
|
288
|
+
3. **Reduce nesting** - Flatten deep conditionals
|
|
289
|
+
4. **Simplify logic** - Use built-in functions
|
|
290
|
+
|
|
291
|
+
## Output Requirements
|
|
292
|
+
|
|
293
|
+
Return JSON containing:
|
|
294
|
+
- `simplified_code`: The simplified version
|
|
295
|
+
- `changes`: List of changes made
|
|
296
|
+
- `summary`: Brief description
|
|
297
|
+
- `rationale`: Explanation of decisions
|
|
298
|
+
- `confidence`: Confidence score [0-1]
|
|
299
|
+
EOF
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Step 3: 编写 schema.json
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
cat > cognitive/modules/code-simplifier/schema.json << 'EOF'
|
|
306
|
+
{
|
|
307
|
+
"$schema": "https://ziel-io.github.io/cognitive-modules/schema/v1.json",
|
|
308
|
+
"input": {
|
|
309
|
+
"type": "object",
|
|
310
|
+
"properties": {
|
|
311
|
+
"code": { "type": "string" },
|
|
312
|
+
"language": { "type": "string" },
|
|
313
|
+
"$ARGUMENTS": { "type": "string" }
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
"output": {
|
|
317
|
+
"type": "object",
|
|
318
|
+
"required": ["simplified_code", "changes", "summary", "rationale", "confidence"],
|
|
319
|
+
"properties": {
|
|
320
|
+
"simplified_code": { "type": "string" },
|
|
321
|
+
"changes": {
|
|
322
|
+
"type": "array",
|
|
323
|
+
"items": {
|
|
324
|
+
"type": "object",
|
|
325
|
+
"properties": {
|
|
326
|
+
"type": { "type": "string" },
|
|
327
|
+
"description": { "type": "string" }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
"summary": { "type": "string" },
|
|
332
|
+
"rationale": { "type": "string" },
|
|
333
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 }
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
EOF
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Step 4: 验证模块
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
cog validate code-simplifier
|
|
344
|
+
cog list # 确认模块出现在列表中
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Step 5: 测试运行
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
cog run code-simplifier --args "def calc(x): if x > 0: if x < 10: return x * 2 else: return x else: return 0" --pretty
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Step 6: 添加示例(可选)
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
mkdir -p cognitive/modules/code-simplifier/examples
|
|
357
|
+
# 添加 input.json 和 output.json 作为测试用例
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 模块设计要点
|
|
361
|
+
|
|
362
|
+
| 要素 | 必须 | 说明 |
|
|
363
|
+
|------|------|------|
|
|
364
|
+
| `name` | ✅ | 唯一标识符,kebab-case |
|
|
365
|
+
| `version` | ✅ | 语义化版本 |
|
|
366
|
+
| `responsibility` | ✅ | 一句话描述职责 |
|
|
367
|
+
| `excludes` | ✅ | 明确列出不做的事 |
|
|
368
|
+
| `$ARGUMENTS` | ✅ | 支持命令行参数 |
|
|
369
|
+
| `confidence` | ✅ | 输出必须包含 0-1 置信度 |
|
|
370
|
+
| `rationale` | ✅ | 输出必须包含推理过程 |
|
|
371
|
+
| `schema.json` | ✅ | 定义输入输出契约 |
|
|
372
|
+
|
|
244
373
|
## 开发
|
|
245
374
|
|
|
246
375
|
```bash
|
|
@@ -254,7 +383,7 @@ pip install -e ".[dev]"
|
|
|
254
383
|
# 运行测试
|
|
255
384
|
pytest tests/ -v
|
|
256
385
|
|
|
257
|
-
#
|
|
386
|
+
# 创建新模块(使用模板)
|
|
258
387
|
cog init my-module -d "模块描述"
|
|
259
388
|
cog validate my-module
|
|
260
389
|
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
cognitive/__init__.py,sha256=uSX5NuOWyW0qtq1bnxbzabZ0OQakAtsjF0MWbjQBvwE,401
|
|
2
|
+
cognitive/cli.py,sha256=q0vHCHFmig9gQ85KRyIrX6vrJZFm5nXF_bW5qQRGEPU,14608
|
|
3
|
+
cognitive/loader.py,sha256=3_ShEfefOMP2EJjpQ1VGTGio4K_GUWLZMQHa0RgvBwg,8715
|
|
4
|
+
cognitive/registry.py,sha256=aBkkpg5PtL_tKDIOXpv6vblB7x_Ax0eVaWgejuQTfCE,8851
|
|
5
|
+
cognitive/runner.py,sha256=KFO_pV7YTluYaL98_5bPp_HTNgwsTvlUjgNYlI8mo-w,8467
|
|
6
|
+
cognitive/subagent.py,sha256=fb7LWwNF6YcJtC_T1dK0EvzqWMBnav-kiCIpvVohEBw,8142
|
|
7
|
+
cognitive/templates.py,sha256=lKC197X9aQIA-npUvVCaplSwvhxjsH_KYVCQtrTZrL4,4712
|
|
8
|
+
cognitive/validator.py,sha256=1v1HUHYOlAc2sYCkIq_gnUMMnca0fdtQwr7UMBFpp04,12200
|
|
9
|
+
cognitive/providers/__init__.py,sha256=hqhVA1IEXpVtyCAteXhO5yD8a8ikQpVIPEKJVHLtRFY,7492
|
|
10
|
+
cognitive_modules-0.3.0.dist-info/licenses/LICENSE,sha256=NXFYUy2hPJdh3NHRxMChTnMiQD9k8zFxkmR7gWefexc,1064
|
|
11
|
+
cognitive_modules-0.3.0.dist-info/METADATA,sha256=PcUIJeGz7wr78MHPEdmDRxqKuFLEzX5aEgpMRHfGN-w,11381
|
|
12
|
+
cognitive_modules-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
+
cognitive_modules-0.3.0.dist-info/entry_points.txt,sha256=PKHlfrFmve5K2349ryipySKbOOsKxo_vIq1NNT-iBV0,42
|
|
14
|
+
cognitive_modules-0.3.0.dist-info/top_level.txt,sha256=kGIfDucCKylo8cRBtxER_v3DHIea-Sol9x9YSJo1u3Y,10
|
|
15
|
+
cognitive_modules-0.3.0.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
cognitive/__init__.py,sha256=uSX5NuOWyW0qtq1bnxbzabZ0OQakAtsjF0MWbjQBvwE,401
|
|
2
|
-
cognitive/cli.py,sha256=q0vHCHFmig9gQ85KRyIrX6vrJZFm5nXF_bW5qQRGEPU,14608
|
|
3
|
-
cognitive/loader.py,sha256=E_lSuy2ERBWfziOHr3f13nLq-wEYvBqIKrGlPk4PQyA,4031
|
|
4
|
-
cognitive/registry.py,sha256=aBkkpg5PtL_tKDIOXpv6vblB7x_Ax0eVaWgejuQTfCE,8851
|
|
5
|
-
cognitive/runner.py,sha256=9uxBAGd3gtXnwUm5x2CjhXi_S1gd2nfjRfM3Q_1f30Q,4362
|
|
6
|
-
cognitive/subagent.py,sha256=fb7LWwNF6YcJtC_T1dK0EvzqWMBnav-kiCIpvVohEBw,8142
|
|
7
|
-
cognitive/templates.py,sha256=fEd1Tj5A12PUE256dC7-cnsulWzm6MTrcQ0yLPWrtx0,4692
|
|
8
|
-
cognitive/validator.py,sha256=1v1HUHYOlAc2sYCkIq_gnUMMnca0fdtQwr7UMBFpp04,12200
|
|
9
|
-
cognitive/providers/__init__.py,sha256=hqhVA1IEXpVtyCAteXhO5yD8a8ikQpVIPEKJVHLtRFY,7492
|
|
10
|
-
cognitive_modules-0.1.1.dist-info/licenses/LICENSE,sha256=NXFYUy2hPJdh3NHRxMChTnMiQD9k8zFxkmR7gWefexc,1064
|
|
11
|
-
cognitive_modules-0.1.1.dist-info/METADATA,sha256=oonLUUD0fO3t7bo8DVZKlqC1vM5ZyHFUw7KXP46SLdI,8242
|
|
12
|
-
cognitive_modules-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
-
cognitive_modules-0.1.1.dist-info/entry_points.txt,sha256=PKHlfrFmve5K2349ryipySKbOOsKxo_vIq1NNT-iBV0,42
|
|
14
|
-
cognitive_modules-0.1.1.dist-info/top_level.txt,sha256=kGIfDucCKylo8cRBtxER_v3DHIea-Sol9x9YSJo1u3Y,10
|
|
15
|
-
cognitive_modules-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|