plm-skill-kernel 1.2.0__tar.gz → 1.4.0__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.
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/PKG-INFO +2 -1
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/__init__.py +1 -1
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/__init__.py +5 -0
- plm_skill_kernel-1.4.0/plm_skill_kernel/executor/_catalog_fixture.py +102 -0
- plm_skill_kernel-1.4.0/plm_skill_kernel/executor/_demo_ids.py +18 -0
- plm_skill_kernel-1.4.0/plm_skill_kernel/executor/_demo_manifest.py +146 -0
- plm_skill_kernel-1.4.0/plm_skill_kernel/executor/_demo_skills.py +195 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/plan.py +8 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/__init__.py +22 -1
- plm_skill_kernel-1.4.0/plm_skill_kernel/mocks/connectors.py +182 -0
- plm_skill_kernel-1.4.0/plm_skill_kernel/mocks/engine_core_stub.py +136 -0
- plm_skill_kernel-1.4.0/plm_skill_kernel/mocks/knowledge_services_port.py +359 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/PKG-INFO +2 -1
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/SOURCES.txt +10 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/requires.txt +1 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/pyproject.toml +10 -1
- plm_skill_kernel-1.4.0/tests/test_connector_read_mock.py +128 -0
- plm_skill_kernel-1.4.0/tests/test_demo_skill_package.py +261 -0
- plm_skill_kernel-1.4.0/tests/test_engine_core_stub_mock.py +141 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_executor.py +1 -1
- plm_skill_kernel-1.4.0/tests/test_knowledge_services_port_mock.py +176 -0
- plm_skill_kernel-1.2.0/plm_skill_kernel/executor/_catalog_fixture.py +0 -80
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/README.md +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/__main__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_cli.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_output_safety.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_serialize.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_terminal.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_verdict.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_version.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/exit_codes.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/models.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/protocol.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/dispatcher.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/binding.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/executor.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/knowledge.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/request.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/selector.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/foundation_seed.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/registry.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/_registry.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/context.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/decorator.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/types.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/server.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/agents/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/agents/suggest.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/bpmn/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/bpmn/generate.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/dedupe.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/normalise.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/documents/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/documents/parse.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/_v1_corpus.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/get_record.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/resolve.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/retrieve.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/search.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/validate.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/__init__.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/knowledge_pack_loader.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/knowledge_search_engine.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/plm_skill_registry.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/plm_tool_definitions.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/skill_orchestrator.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/dependency_links.txt +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/entry_points.txt +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/top_level.txt +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/setup.cfg +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_agents_suggest_asgi.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_agents_suggest_unit.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_dispatcher.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_documents_parse_asgi.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_documents_parse_unit.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_foundation_mocks.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_sdk_decorator.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_server.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_skills_cleansing.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_skills_knowledge.py +0 -0
- {plm_skill_kernel-1.2.0 → plm_skill_kernel-1.4.0}/tests/test_skills_knowledge_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plm-skill-kernel
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: TracePulse PLM Skill Kernel — Wave 2 Conv A. Decorator-based Skill SDK + V1 in-process dispatcher + Kernel HTTP server stub matching CR.10 §7.bis (POST /v1/skills/{id}/invoke). Ships 3 starter skills (cleansing.normalise, cleansing.dedupe, bpmn.generate). Sibling of plm-engine-core; the two communicate over the V1.1 HTTP loopback (SKILL_KERNEL_LOOPBACK=on) — no Python-level coupling at the dispatch boundary.
|
|
5
5
|
Author: TracePulse
|
|
6
6
|
License: Proprietary
|
|
@@ -13,6 +13,7 @@ Requires-Dist: uvicorn>=0.27
|
|
|
13
13
|
Requires-Dist: httpx>=0.27.0
|
|
14
14
|
Requires-Dist: plm-engine-core
|
|
15
15
|
Requires-Dist: plm-knowledge
|
|
16
|
+
Requires-Dist: plm-connector-sdk<2,>=1.1
|
|
16
17
|
Provides-Extra: test
|
|
17
18
|
Requires-Dist: pytest; extra == "test"
|
|
18
19
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
@@ -14,7 +14,7 @@ sanctioned interaction is the V1.1 HTTP loopback (env-flagged on
|
|
|
14
14
|
``SKILL_KERNEL_LOOPBACK``); any Python-level reach-in is forbidden by
|
|
15
15
|
the import-linter contract in ``pyproject.toml``.
|
|
16
16
|
"""
|
|
17
|
-
__version__ = "1.
|
|
17
|
+
__version__ = "1.4.0"
|
|
18
18
|
|
|
19
19
|
# The decorator + sdk surface is the public authoring API. Re-exported
|
|
20
20
|
# here so callers write ``from plm_skill_kernel import skill`` rather
|
|
@@ -23,6 +23,11 @@ path; the executor is driven by the demo entrypoint + tests.
|
|
|
23
23
|
"""
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
|
+
# NOTE: ``load_demo_skills`` is intentionally NOT re-exported here. Importing it
|
|
27
|
+
# would fire the demo tasks' ``@skill`` decorators as a side effect of importing
|
|
28
|
+
# the executor package, leaking ``demo.foundation.*`` into the SDK registry /
|
|
29
|
+
# federation introspection. Demo registration is a deliberate opt-in — import
|
|
30
|
+
# ``plm_skill_kernel.executor._demo_skills.load_demo_skills`` and call it.
|
|
26
31
|
from .binding import BindingNotFound, resolve_binding
|
|
27
32
|
from .executor import SkillExecutor, TaskFailure
|
|
28
33
|
from .knowledge import EchoKnowledgeProvider, KnowledgeProvider, NullKnowledgeProvider
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Demo catalog fixture (FTR-2232 / E1; the certified package is FTR-2235 / E4).
|
|
2
|
+
|
|
3
|
+
# MOCK(MVP-DEMO): this is a demo-scoped skill *catalog*, not the governed
|
|
4
|
+
# skill registry or the ``manifest-profile/1.0`` authoring surface. It maps a
|
|
5
|
+
# composite skill id to its ordered :class:`SkillPlan`. E1 shipped a
|
|
6
|
+
# structurally-valid stub here; **E4** replaces it with the concrete, certified
|
|
7
|
+
# demo skill package: an ordered >= 2-task composite that carries all manifest
|
|
8
|
+
# attributes and runs end-to-end over the E2 pump foundation seam (decision C1).
|
|
9
|
+
|
|
10
|
+
The composite ``demo.knowledge_lookup`` decomposes into two ordered tasks:
|
|
11
|
+
|
|
12
|
+
1. ``demo.foundation.retrieve`` — the executor injects a broad set of
|
|
13
|
+
foundation snippets (via the task's static ``knowledge_query`` seed); the
|
|
14
|
+
task narrows them to the runtime query.
|
|
15
|
+
2. ``demo.foundation.synthesize`` — turns the retrieved snippets into a
|
|
16
|
+
grounded, cited answer.
|
|
17
|
+
|
|
18
|
+
GOVERNANCE FIREWALL (FTR-2235 AC): ``certified`` / ``controlled`` and the
|
|
19
|
+
attached :data:`~plm_skill_kernel.executor._demo_manifest.DEMO_MANIFEST` are
|
|
20
|
+
**demo-fixture fakes** — advisory, unenforced, and confined to this layer.
|
|
21
|
+
They must never reach the FTR-2034 register or the published profile.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from typing import Dict, Tuple
|
|
26
|
+
|
|
27
|
+
from ._demo_ids import DEMO_TASK_VERSION, RETRIEVE_SKILL_ID, SYNTHESIZE_SKILL_ID
|
|
28
|
+
from ._demo_manifest import DEMO_MANIFEST
|
|
29
|
+
from .plan import SkillPlan, TaskStep
|
|
30
|
+
|
|
31
|
+
DEMO_SKILL_ID = "demo.knowledge_lookup"
|
|
32
|
+
DEMO_SKILL_VERSION = "0.1.0"
|
|
33
|
+
|
|
34
|
+
# MOCK(MVP-DEMO): static seed the executor hands the knowledge provider for
|
|
35
|
+
# task 1 — a broad "load the foundation context" query. It is intentionally
|
|
36
|
+
# NOT the runtime query: the executor loads a broad set, then the certified
|
|
37
|
+
# skill's task 1 narrows it to what the caller actually asked. Broad on
|
|
38
|
+
# purpose so re-ranking in the task is meaningful.
|
|
39
|
+
_FOUNDATION_SEED_QUERY = (
|
|
40
|
+
"hydraulic pump foundation reference material standard torque pressure shaft"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
_DEMO_PLAN = SkillPlan(
|
|
44
|
+
skill_id=DEMO_SKILL_ID,
|
|
45
|
+
version=DEMO_SKILL_VERSION,
|
|
46
|
+
description=(
|
|
47
|
+
"Certified demo composite: retrieve grounding from the foundation "
|
|
48
|
+
"corpus, then synthesize a cited answer — an ordered two-task skill "
|
|
49
|
+
"the agent delegates to instead of answering from itself."
|
|
50
|
+
),
|
|
51
|
+
# MOCK(MVP-DEMO): fixture governance flags — advisory, not real
|
|
52
|
+
# certification (real status lives in the FTR-2034 register).
|
|
53
|
+
certified=True,
|
|
54
|
+
controlled=True,
|
|
55
|
+
# MOCK(MVP-DEMO): all manifest-profile/1.0 attributes, faked (AC1).
|
|
56
|
+
manifest=DEMO_MANIFEST,
|
|
57
|
+
tasks=(
|
|
58
|
+
TaskStep(
|
|
59
|
+
task_id="retrieve",
|
|
60
|
+
skill_id=RETRIEVE_SKILL_ID,
|
|
61
|
+
version=DEMO_TASK_VERSION,
|
|
62
|
+
description=(
|
|
63
|
+
"Ordered task 1 — narrow the injected foundation snippets to "
|
|
64
|
+
"the runtime query."
|
|
65
|
+
),
|
|
66
|
+
input_keys=("query",),
|
|
67
|
+
# Triggers foundation injection under payload['foundation'].
|
|
68
|
+
knowledge_query=_FOUNDATION_SEED_QUERY,
|
|
69
|
+
),
|
|
70
|
+
TaskStep(
|
|
71
|
+
task_id="synthesize",
|
|
72
|
+
skill_id=SYNTHESIZE_SKILL_ID,
|
|
73
|
+
version=DEMO_TASK_VERSION,
|
|
74
|
+
description=(
|
|
75
|
+
"Ordered task 2 — synthesize a grounded, cited answer from "
|
|
76
|
+
"the retrieved snippets."
|
|
77
|
+
),
|
|
78
|
+
input_keys=("retrieved", "query"),
|
|
79
|
+
output_key="result",
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Catalog is keyed by (skill_id, version); a None-version lookup resolves to
|
|
86
|
+
# the sole registered version for that id (demo convenience).
|
|
87
|
+
DEMO_CATALOG: Dict[Tuple[str, str], SkillPlan] = {
|
|
88
|
+
(DEMO_SKILL_ID, DEMO_SKILL_VERSION): _DEMO_PLAN,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_demo_catalog() -> Dict[Tuple[str, str], SkillPlan]:
|
|
93
|
+
"""Return a fresh copy of the demo catalog (callers may extend it)."""
|
|
94
|
+
return dict(DEMO_CATALOG)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
__all__ = [
|
|
98
|
+
"DEMO_CATALOG",
|
|
99
|
+
"DEMO_SKILL_ID",
|
|
100
|
+
"DEMO_SKILL_VERSION",
|
|
101
|
+
"build_demo_catalog",
|
|
102
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Shared ids for the demo composite's tasks (FTR-2235 / E4).
|
|
2
|
+
|
|
3
|
+
Deliberately **dependency-free**. The catalog fixture names the demo tasks by
|
|
4
|
+
these id strings *without* importing the skill module — because importing
|
|
5
|
+
:mod:`plm_skill_kernel.executor._demo_skills` fires its ``@skill`` decorators
|
|
6
|
+
and registers the demo callables into the SDK registry. Keeping the ids here
|
|
7
|
+
means merely importing the ``executor`` package (or the catalog fixture) never
|
|
8
|
+
has that side effect; registration is a deliberate act via
|
|
9
|
+
:func:`plm_skill_kernel.executor._demo_skills.load_demo_skills`, so faked demo
|
|
10
|
+
skills never boot-load or appear in federation introspection unasked.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
RETRIEVE_SKILL_ID = "demo.foundation.retrieve"
|
|
15
|
+
SYNTHESIZE_SKILL_ID = "demo.foundation.synthesize"
|
|
16
|
+
DEMO_TASK_VERSION = "1.0.0"
|
|
17
|
+
|
|
18
|
+
__all__ = ["DEMO_TASK_VERSION", "RETRIEVE_SKILL_ID", "SYNTHESIZE_SKILL_ID"]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Demo manifest attributes for the certified composite (FTR-2235 / E4).
|
|
2
|
+
|
|
3
|
+
# MOCK(MVP-DEMO): every value in this module is a **demo-fixture fake**. This
|
|
4
|
+
# is the "carries all manifest attributes" surface of the demo skill package
|
|
5
|
+
# (AC1) — it mirrors the *field names* of the governed
|
|
6
|
+
# ``manifest-profile/1.0`` (read-only source:
|
|
7
|
+
# ``plm_skill_packages/manifest_profile/data/v1_profile.yaml``) so the demo
|
|
8
|
+
# fixture is recognisably a full manifest, while every value is a stand-in.
|
|
9
|
+
#
|
|
10
|
+
# GOVERNANCE FIREWALL (FTR-2235 AC): the faked ``certified`` / ``maturity``
|
|
11
|
+
# values below live ONLY in this demo fixture layer. They MUST NOT be written
|
|
12
|
+
# to the FTR-2034 decision register or the published ``manifest-profile/1.0``.
|
|
13
|
+
# The value string ``certified_industrial_capability`` is deliberately
|
|
14
|
+
# confined to this module (a firewall guard test asserts it appears nowhere
|
|
15
|
+
# else under ``plm_skill_kernel``).
|
|
16
|
+
|
|
17
|
+
The model is ``frozen`` + ``extra="forbid"`` to mirror the discipline of the
|
|
18
|
+
governed profile, but it is a demo descriptor — it is never validated against,
|
|
19
|
+
or promoted into, the real profile. It carries no behaviour; the executor
|
|
20
|
+
never reads it (AC4: no conformance gating).
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from typing import Tuple
|
|
25
|
+
|
|
26
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
27
|
+
|
|
28
|
+
# The faked maturity/lifecycle/autonomy values. Named constants so the intent
|
|
29
|
+
# is legible and the firewall guard has one obvious place to look.
|
|
30
|
+
# MOCK(MVP-DEMO): a real certified maturity requires human sign-off in the
|
|
31
|
+
# FTR-2034 register; this string is a fixture stand-in and nothing else.
|
|
32
|
+
_MOCK_MATURITY_CERTIFIED = "certified_industrial_capability"
|
|
33
|
+
_MOCK_LIFECYCLE_RELEASED = "released"
|
|
34
|
+
_MOCK_AUTONOMY_SUPERVISED = "supervised"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DemoManifestAttributes(BaseModel):
|
|
38
|
+
"""All ``manifest-profile/1.0`` attributes, mirrored as demo metadata.
|
|
39
|
+
|
|
40
|
+
One field per governed profile ``machine_key`` (28 total). Presence — not
|
|
41
|
+
enforcement — is the point: AC1 asks that the demo skill "carries all
|
|
42
|
+
manifest attributes", and AC4 explicitly says the executor does not gate
|
|
43
|
+
on a ``VALID`` conformance verdict, so these values are never validated.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
47
|
+
|
|
48
|
+
# ── identity ──────────────────────────────────────────────────────────
|
|
49
|
+
skill_id: str
|
|
50
|
+
name: str
|
|
51
|
+
mission: str
|
|
52
|
+
semantic_version: str
|
|
53
|
+
|
|
54
|
+
# ── lifecycle / maturity / autonomy (the "certified" story) ───────────
|
|
55
|
+
lifecycle_state: str
|
|
56
|
+
maturity_level: str
|
|
57
|
+
autonomy_ceiling: str
|
|
58
|
+
|
|
59
|
+
# ── scope / ownership / audience ──────────────────────────────────────
|
|
60
|
+
tenant_scope: str
|
|
61
|
+
audience: str
|
|
62
|
+
owner: str
|
|
63
|
+
|
|
64
|
+
# ── governance references (certified-tier fields) ─────────────────────
|
|
65
|
+
minimum_trace_level: str
|
|
66
|
+
hitl_policy_reference: str
|
|
67
|
+
certification_reference: str
|
|
68
|
+
evaluation_reference: str
|
|
69
|
+
pinned_baseline_reference: str
|
|
70
|
+
|
|
71
|
+
# ── contract / schema surface ─────────────────────────────────────────
|
|
72
|
+
contract_hash: str
|
|
73
|
+
input_schema_ref: str
|
|
74
|
+
output_schema_ref: str
|
|
75
|
+
|
|
76
|
+
# ── behavioural contract ──────────────────────────────────────────────
|
|
77
|
+
preconditions: Tuple[str, ...] = Field(default=())
|
|
78
|
+
postconditions: Tuple[str, ...] = Field(default=())
|
|
79
|
+
failure_modes: Tuple[str, ...] = Field(default=())
|
|
80
|
+
evidence_expectations: Tuple[str, ...] = Field(default=())
|
|
81
|
+
|
|
82
|
+
# ── compatibility / dependencies ──────────────────────────────────────
|
|
83
|
+
kernel_api_compatibility: str
|
|
84
|
+
knowledge_api_compatibility: str
|
|
85
|
+
model_configuration: str
|
|
86
|
+
connector_deps: Tuple[str, ...] = Field(default=())
|
|
87
|
+
inference_deps: Tuple[str, ...] = Field(default=())
|
|
88
|
+
knowledge_deps: Tuple[str, ...] = Field(default=())
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# MOCK(MVP-DEMO): the concrete faked manifest for the demo composite skill.
|
|
92
|
+
# Certified-tier so the demo can *show* an agent delegating to a certified,
|
|
93
|
+
# controlled skill — every value here is a stand-in, none is ratified.
|
|
94
|
+
DEMO_MANIFEST = DemoManifestAttributes(
|
|
95
|
+
skill_id="demo.knowledge_lookup",
|
|
96
|
+
name="Foundation Knowledge Lookup (DEMO)",
|
|
97
|
+
mission=(
|
|
98
|
+
"Delegate a knowledge request to an ordered, certified composite "
|
|
99
|
+
"skill that retrieves and then synthesizes grounded foundation "
|
|
100
|
+
"reference material — instead of the agent answering from itself."
|
|
101
|
+
),
|
|
102
|
+
semantic_version="0.1.0",
|
|
103
|
+
lifecycle_state=_MOCK_LIFECYCLE_RELEASED,
|
|
104
|
+
maturity_level=_MOCK_MATURITY_CERTIFIED,
|
|
105
|
+
autonomy_ceiling=_MOCK_AUTONOMY_SUPERVISED,
|
|
106
|
+
tenant_scope="shared",
|
|
107
|
+
audience="internal-demo",
|
|
108
|
+
owner="mvp-demo-owner (MOCK)",
|
|
109
|
+
minimum_trace_level="standard",
|
|
110
|
+
hitl_policy_reference="mock://hitl-policy/none",
|
|
111
|
+
certification_reference="mock://certification/demo-knowledge-lookup@0.1.0",
|
|
112
|
+
evaluation_reference="mock://evaluation/demo-knowledge-lookup@0.1.0",
|
|
113
|
+
pinned_baseline_reference="mock://baseline/foundation-seed@rev-1",
|
|
114
|
+
contract_hash="sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
115
|
+
input_schema_ref="mock://schema/demo.knowledge_lookup/input@0.1.0",
|
|
116
|
+
output_schema_ref="mock://schema/demo.knowledge_lookup/output@0.1.0",
|
|
117
|
+
preconditions=(
|
|
118
|
+
"A foundation knowledge provider is wired on the executor "
|
|
119
|
+
"(demo: SeedFoundationKnowledgeProvider behind PLM_MVP_MOCKS).",
|
|
120
|
+
),
|
|
121
|
+
postconditions=(
|
|
122
|
+
"Returns a grounded answer citing foundation snippet ids, or an "
|
|
123
|
+
"explicitly empty grounding when nothing matched the query.",
|
|
124
|
+
),
|
|
125
|
+
failure_modes=(
|
|
126
|
+
"No foundation snippet matches the query -> empty grounding, the "
|
|
127
|
+
"composite still succeeds (the demo does not fail closed on no-hit).",
|
|
128
|
+
),
|
|
129
|
+
evidence_expectations=(
|
|
130
|
+
"Per-task execution trace with per-task knowledge-injection flags.",
|
|
131
|
+
),
|
|
132
|
+
kernel_api_compatibility="v1",
|
|
133
|
+
knowledge_api_compatibility="v1",
|
|
134
|
+
model_configuration="none (deterministic; no inference in the demo composite)",
|
|
135
|
+
connector_deps=(),
|
|
136
|
+
inference_deps=(),
|
|
137
|
+
knowledge_deps=("foundation-seed (MOCK)",),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def manifest_field_names() -> Tuple[str, ...]:
|
|
142
|
+
"""Return the mirrored profile attribute names (for AC1 assertions)."""
|
|
143
|
+
return tuple(DemoManifestAttributes.model_fields.keys())
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
__all__ = ["DEMO_MANIFEST", "DemoManifestAttributes", "manifest_field_names"]
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""The certified composite's ordered tasks (FTR-2235 / E4, decision C1).
|
|
2
|
+
|
|
3
|
+
# MOCK(MVP-DEMO): two demo-scoped ``@skill`` tasks that make the composite
|
|
4
|
+
# ``demo.knowledge_lookup`` do real, data-dependent work over the E2 pump
|
|
5
|
+
# foundation seam — one coherent dataset, no ``extra="forbid"`` collision
|
|
6
|
+
# (both tasks read the injected ``payload['foundation']`` freely, unlike the
|
|
7
|
+
# strict ``knowledge.*`` request models).
|
|
8
|
+
|
|
9
|
+
The composite's ordered steps:
|
|
10
|
+
|
|
11
|
+
* **task 1 — ``demo.foundation.retrieve``**: the executor has already loaded a
|
|
12
|
+
broad set of foundation snippets into ``payload['foundation']`` (via the
|
|
13
|
+
task's static ``knowledge_query`` seed). This task *narrows* that set to the
|
|
14
|
+
caller's runtime ``query`` by token overlap and keeps the top matches.
|
|
15
|
+
* **task 2 — ``demo.foundation.synthesize``**: turns the retrieved snippets
|
|
16
|
+
into a short, grounded, pump-themed answer with explicit citations
|
|
17
|
+
(snippet id + title + non-authoritative source label).
|
|
18
|
+
|
|
19
|
+
Both are authored with the ordinary ``@skill`` decorator (Decision #59 shape)
|
|
20
|
+
so they register into the SDK registry and run through the ordinary
|
|
21
|
+
dispatcher — nothing about the executor changes. They are **deliberately kept
|
|
22
|
+
out of** :func:`plm_skill_kernel.registry.load_starter_skills` (the production
|
|
23
|
+
/ federation-manifest surface); the demo wires them via
|
|
24
|
+
:func:`load_demo_skills` instead, so faked demo skills never boot-load into a
|
|
25
|
+
real kernel.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import re
|
|
30
|
+
from typing import Any, Dict, List
|
|
31
|
+
|
|
32
|
+
from plm_skill_kernel import SkillContext, skill
|
|
33
|
+
from plm_skill_kernel.sdk._registry import RegisteredSkill, get_registry
|
|
34
|
+
|
|
35
|
+
from ._demo_ids import DEMO_TASK_VERSION, RETRIEVE_SKILL_ID, SYNTHESIZE_SKILL_ID
|
|
36
|
+
|
|
37
|
+
# How many narrowed snippets task 1 forwards to task 2.
|
|
38
|
+
_KEEP_TOP = 3
|
|
39
|
+
_TOKEN_RE = re.compile(r"[a-z0-9]+")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _tokens(text: str) -> List[str]:
|
|
43
|
+
return _TOKEN_RE.findall((text or "").lower())
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _snippet_blob(snippet: Dict[str, Any]) -> str:
|
|
47
|
+
"""Searchable text for a foundation snippet (id/type/title/snippet)."""
|
|
48
|
+
parts = [
|
|
49
|
+
str(snippet.get("id", "")),
|
|
50
|
+
str(snippet.get("type", "")),
|
|
51
|
+
str(snippet.get("title", "")),
|
|
52
|
+
str(snippet.get("snippet", "")),
|
|
53
|
+
]
|
|
54
|
+
return " ".join(parts).lower()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@skill(
|
|
58
|
+
id=RETRIEVE_SKILL_ID,
|
|
59
|
+
version=DEMO_TASK_VERSION,
|
|
60
|
+
description=(
|
|
61
|
+
"Demo composite task 1: narrow the injected foundation snippets to "
|
|
62
|
+
"the runtime query by token overlap; keep the top matches."
|
|
63
|
+
),
|
|
64
|
+
min_autonomy="L1",
|
|
65
|
+
)
|
|
66
|
+
async def demo_foundation_retrieve(
|
|
67
|
+
payload: Dict[str, Any], ctx: SkillContext
|
|
68
|
+
) -> Dict[str, Any]:
|
|
69
|
+
"""Rank ``payload['foundation']`` against ``payload['query']``.
|
|
70
|
+
|
|
71
|
+
Reads the foundation snippets the executor injected under the reserved
|
|
72
|
+
``foundation`` key and re-ranks them for *this* request's query. Returns
|
|
73
|
+
the top matches under ``retrieved`` (shallow-merged into the running
|
|
74
|
+
state for task 2). Never fails closed on a no-hit — an empty ``retrieved``
|
|
75
|
+
is a valid, grounded-in-nothing outcome (AC4: the demo does not gate).
|
|
76
|
+
"""
|
|
77
|
+
query = str(payload.get("query", ""))
|
|
78
|
+
foundation = payload.get("foundation") or []
|
|
79
|
+
q = set(_tokens(query))
|
|
80
|
+
|
|
81
|
+
scored: List[tuple] = []
|
|
82
|
+
for snippet in foundation:
|
|
83
|
+
if not isinstance(snippet, dict):
|
|
84
|
+
continue
|
|
85
|
+
blob = _snippet_blob(snippet)
|
|
86
|
+
overlap = sum(1 for tok in q if tok in blob)
|
|
87
|
+
if overlap > 0:
|
|
88
|
+
scored.append((overlap, snippet))
|
|
89
|
+
|
|
90
|
+
# Deterministic order: higher overlap first, then snippet id ascending.
|
|
91
|
+
scored.sort(key=lambda s: (-s[0], str(s[1].get("id", ""))))
|
|
92
|
+
retrieved = [snippet for _, snippet in scored[:_KEEP_TOP]]
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
"retrieved": retrieved,
|
|
96
|
+
"match_count": len(scored),
|
|
97
|
+
"considered": len(foundation),
|
|
98
|
+
"query_used": query,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@skill(
|
|
103
|
+
id=SYNTHESIZE_SKILL_ID,
|
|
104
|
+
version=DEMO_TASK_VERSION,
|
|
105
|
+
description=(
|
|
106
|
+
"Demo composite task 2: synthesize a short grounded answer from the "
|
|
107
|
+
"retrieved foundation snippets, with explicit citations."
|
|
108
|
+
),
|
|
109
|
+
min_autonomy="L1",
|
|
110
|
+
)
|
|
111
|
+
async def demo_foundation_synthesize(
|
|
112
|
+
payload: Dict[str, Any], ctx: SkillContext
|
|
113
|
+
) -> Dict[str, Any]:
|
|
114
|
+
"""Compose a grounded, pump-themed answer from ``payload['retrieved']``.
|
|
115
|
+
|
|
116
|
+
The answer text names the top foundation reference(s) and every claim is
|
|
117
|
+
tied to a citation (snippet id + title + the non-authoritative ``source``
|
|
118
|
+
label). If nothing was retrieved, the answer says so plainly and
|
|
119
|
+
``grounded`` is ``False`` — a legible, honest empty result.
|
|
120
|
+
"""
|
|
121
|
+
query = str(payload.get("query", ""))
|
|
122
|
+
retrieved = payload.get("retrieved") or []
|
|
123
|
+
|
|
124
|
+
citations = [
|
|
125
|
+
{
|
|
126
|
+
"id": snippet.get("id"),
|
|
127
|
+
"title": snippet.get("title"),
|
|
128
|
+
"source": snippet.get("source"),
|
|
129
|
+
}
|
|
130
|
+
for snippet in retrieved
|
|
131
|
+
if isinstance(snippet, dict)
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
if citations:
|
|
135
|
+
lead = retrieved[0]
|
|
136
|
+
titles = ", ".join(
|
|
137
|
+
f"{c['title']} [{c['id']}]" for c in citations if c.get("title")
|
|
138
|
+
)
|
|
139
|
+
answer = (
|
|
140
|
+
f"For '{query}': the most relevant foundation reference is "
|
|
141
|
+
f"{lead.get('title')} [{lead.get('id')}]. "
|
|
142
|
+
f"Grounded on {len(citations)} foundation snippet(s): {titles}."
|
|
143
|
+
)
|
|
144
|
+
grounded = True
|
|
145
|
+
else:
|
|
146
|
+
answer = (
|
|
147
|
+
f"For '{query}': no foundation reference matched the query, so no "
|
|
148
|
+
"grounded answer could be produced from the demo corpus."
|
|
149
|
+
)
|
|
150
|
+
grounded = False
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
"answer": answer,
|
|
154
|
+
"citations": citations,
|
|
155
|
+
"grounded": grounded,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# Capture the exact registry entries the decorators built at import time (with
|
|
160
|
+
# their full metadata + the live callables). ``load_demo_skills`` re-registers
|
|
161
|
+
# these same callables, so demo registration survives a ``registry.clear()``
|
|
162
|
+
# in a test fixture — Python's module cache would not re-fire the decorators.
|
|
163
|
+
_DEMO_ENTRIES = tuple(
|
|
164
|
+
entry
|
|
165
|
+
for entry in (
|
|
166
|
+
get_registry().resolve(RETRIEVE_SKILL_ID, DEMO_TASK_VERSION),
|
|
167
|
+
get_registry().resolve(SYNTHESIZE_SKILL_ID, DEMO_TASK_VERSION),
|
|
168
|
+
)
|
|
169
|
+
if entry is not None
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def load_demo_skills() -> List[RegisteredSkill]:
|
|
174
|
+
"""(Re)register the demo composite's tasks into the SDK registry.
|
|
175
|
+
|
|
176
|
+
Idempotent: the registry tolerates same-callable re-registration, so this
|
|
177
|
+
is safe to call after a ``registry.clear()`` (e.g. the ``fresh_registry``
|
|
178
|
+
test fixture) to restore the demo tasks. Kept separate from
|
|
179
|
+
:func:`plm_skill_kernel.registry.load_starter_skills` on purpose — demo
|
|
180
|
+
skills must never boot-load into a real kernel.
|
|
181
|
+
"""
|
|
182
|
+
registry = get_registry()
|
|
183
|
+
for entry in _DEMO_ENTRIES:
|
|
184
|
+
registry.register(entry)
|
|
185
|
+
return list(_DEMO_ENTRIES)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
__all__ = [
|
|
189
|
+
"DEMO_TASK_VERSION",
|
|
190
|
+
"RETRIEVE_SKILL_ID",
|
|
191
|
+
"SYNTHESIZE_SKILL_ID",
|
|
192
|
+
"demo_foundation_retrieve",
|
|
193
|
+
"demo_foundation_synthesize",
|
|
194
|
+
"load_demo_skills",
|
|
195
|
+
]
|
|
@@ -23,6 +23,8 @@ from typing import Any, Dict, Optional, Tuple
|
|
|
23
23
|
|
|
24
24
|
from pydantic import BaseModel, ConfigDict, Field
|
|
25
25
|
|
|
26
|
+
from ._demo_manifest import DemoManifestAttributes
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
class TaskStep(BaseModel):
|
|
28
30
|
"""One ordered task inside a :class:`SkillPlan`.
|
|
@@ -102,6 +104,12 @@ class SkillPlan(BaseModel):
|
|
|
102
104
|
# register + published profile, never here. Present-but-unenforced.
|
|
103
105
|
certified: bool = False
|
|
104
106
|
controlled: bool = False
|
|
107
|
+
# MOCK(MVP-DEMO): optional mirror of the ``manifest-profile/1.0``
|
|
108
|
+
# attributes, present so the demo skill "carries all manifest attributes"
|
|
109
|
+
# (FTR-2235 AC1). Every value is a fixture fake; the executor never reads
|
|
110
|
+
# it (AC4: no conformance gating). Backward-compatible default ``None`` so
|
|
111
|
+
# E1 plans and tests that omit it are unaffected.
|
|
112
|
+
manifest: Optional[DemoManifestAttributes] = None
|
|
105
113
|
|
|
106
114
|
def task_ids(self) -> Tuple[str, ...]:
|
|
107
115
|
return tuple(t.task_id for t in self.tasks)
|
|
@@ -50,4 +50,25 @@ def require_mocks(what: str = "this mock") -> None:
|
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
# ── governed contract package versions this mocks layer targets (AC4) ──────
|
|
54
|
+
# The MVP mocks stand in for three governed, separately-versioned packages. This
|
|
55
|
+
# dict is the single authoritative record of the exact versions the mocks were
|
|
56
|
+
# written against; ``tests/contracts/test_mvp_mocks_boundary.py`` asserts each
|
|
57
|
+
# against what is actually installed. A governed-package bump therefore surfaces
|
|
58
|
+
# as a failing pin that a human re-syncs on purpose (updating MOCKS.md), never a
|
|
59
|
+
# silent absorption. Per-mock *shape* pins (KS_CONTRACT_VERSION,
|
|
60
|
+
# CONNECTOR_SDK_VERSION, SELECTION_REQUIRED_KEYS) live in their own modules.
|
|
61
|
+
GOVERNED_CONTRACT_VERSIONS = {
|
|
62
|
+
"plm-shared": "1.2.1", # CR.10/CR.12 wire shapes the SDK + dispatcher consume
|
|
63
|
+
"plm-connector-sdk": "1.3.1", # ConnectorReadResult — AC2 reaches a connector through it
|
|
64
|
+
"plm-engine-core": "1.6.0", # runtime.llm_service — the AC3 stub stands in for it
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"GOVERNED_CONTRACT_VERSIONS",
|
|
70
|
+
"MOCKS_ENV",
|
|
71
|
+
"MocksDisabled",
|
|
72
|
+
"mocks_enabled",
|
|
73
|
+
"require_mocks",
|
|
74
|
+
]
|