plm-skill-kernel 1.3.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.3.0 → plm_skill_kernel-1.4.0}/PKG-INFO +1 -1
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/__init__.py +1 -1
- {plm_skill_kernel-1.3.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.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/plan.py +8 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/PKG-INFO +1 -1
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/SOURCES.txt +4 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/pyproject.toml +1 -1
- plm_skill_kernel-1.4.0/tests/test_demo_skill_package.py +261 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_executor.py +1 -1
- plm_skill_kernel-1.3.0/plm_skill_kernel/executor/_catalog_fixture.py +0 -80
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/README.md +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/__main__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_cli.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_output_safety.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_serialize.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_terminal.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_verdict.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_version.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/exit_codes.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/models.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/protocol.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/dispatcher.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/binding.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/executor.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/knowledge.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/request.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/executor/selector.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/connectors.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/engine_core_stub.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/foundation_seed.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/knowledge_services_port.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/registry.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/_registry.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/context.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/decorator.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/sdk/types.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/server.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/agents/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/agents/suggest.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/bpmn/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/bpmn/generate.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/dedupe.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/normalise.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/documents/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/documents/parse.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/_v1_corpus.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/get_record.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/resolve.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/retrieve.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/search.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/validate.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/__init__.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/knowledge_pack_loader.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/knowledge_search_engine.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/plm_skill_registry.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/plm_tool_definitions.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/skill_orchestrator.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/dependency_links.txt +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/entry_points.txt +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/requires.txt +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/top_level.txt +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/setup.cfg +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_agents_suggest_asgi.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_agents_suggest_unit.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_connector_read_mock.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_dispatcher.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_documents_parse_asgi.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_documents_parse_unit.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_engine_core_stub_mock.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_foundation_mocks.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_knowledge_services_port_mock.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_sdk_decorator.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_server.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_skills_cleansing.py +0 -0
- {plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_skills_knowledge.py +0 -0
- {plm_skill_kernel-1.3.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
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -23,6 +23,9 @@ plm_skill_kernel/conformance/models.py
|
|
|
23
23
|
plm_skill_kernel/conformance/protocol.py
|
|
24
24
|
plm_skill_kernel/executor/__init__.py
|
|
25
25
|
plm_skill_kernel/executor/_catalog_fixture.py
|
|
26
|
+
plm_skill_kernel/executor/_demo_ids.py
|
|
27
|
+
plm_skill_kernel/executor/_demo_manifest.py
|
|
28
|
+
plm_skill_kernel/executor/_demo_skills.py
|
|
26
29
|
plm_skill_kernel/executor/binding.py
|
|
27
30
|
plm_skill_kernel/executor/executor.py
|
|
28
31
|
plm_skill_kernel/executor/knowledge.py
|
|
@@ -65,6 +68,7 @@ plm_skill_kernel/skills_legacy/skill_orchestrator.py
|
|
|
65
68
|
tests/test_agents_suggest_asgi.py
|
|
66
69
|
tests/test_agents_suggest_unit.py
|
|
67
70
|
tests/test_connector_read_mock.py
|
|
71
|
+
tests/test_demo_skill_package.py
|
|
68
72
|
tests/test_dispatcher.py
|
|
69
73
|
tests/test_documents_parse_asgi.py
|
|
70
74
|
tests/test_documents_parse_unit.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "plm-skill-kernel"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.0"
|
|
8
8
|
description = "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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Tests for the certified demo skill package (FTR-2235 / E4).
|
|
2
|
+
|
|
3
|
+
Covers the ratified acceptance criteria:
|
|
4
|
+
|
|
5
|
+
* **AC1** — the demo skill carries *all* manifest attributes: the fixture
|
|
6
|
+
manifest mirrors every ``manifest-profile/1.0`` machine key (28 total) and
|
|
7
|
+
is attached to the composite plan alongside the certified/controlled flags.
|
|
8
|
+
* **AC2** — the skill is marked certified/controlled via the inline-marked
|
|
9
|
+
fixture manifest AND registered in the catalog fixture.
|
|
10
|
+
* **AC3** — request → executor → ordered tasks → foundation knowledge →
|
|
11
|
+
grounded, cited result wires end-to-end, and the agent *delegates* to the
|
|
12
|
+
certified composite rather than answering itself.
|
|
13
|
+
* **AC4** — with the profile non-effective, the executor does NOT hard-gate on
|
|
14
|
+
a ``VALID`` conformance verdict: a plan with a full fixture manifest and one
|
|
15
|
+
with no manifest at all run identically.
|
|
16
|
+
|
|
17
|
+
Plus a **governance-firewall guard**: the faked ``certified`` maturity string
|
|
18
|
+
lives only in the demo fixture module and nowhere else under the package.
|
|
19
|
+
|
|
20
|
+
The demo tasks are quarantined out of ``load_starter_skills``; every test that
|
|
21
|
+
runs the composite loads them deliberately via ``load_demo_skills`` into a
|
|
22
|
+
cleared registry, exactly as the demo entrypoint does.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
|
|
28
|
+
import pytest
|
|
29
|
+
|
|
30
|
+
from plm_skill_kernel import SkillContext
|
|
31
|
+
from plm_skill_kernel.dispatcher import InProcessDispatcher
|
|
32
|
+
from plm_skill_kernel.executor import SkillExecutor, parse_request, resolve_binding, select_skill
|
|
33
|
+
from plm_skill_kernel.executor._catalog_fixture import (
|
|
34
|
+
DEMO_SKILL_ID,
|
|
35
|
+
DEMO_SKILL_VERSION,
|
|
36
|
+
build_demo_catalog,
|
|
37
|
+
)
|
|
38
|
+
from plm_skill_kernel.executor._demo_manifest import DEMO_MANIFEST, DemoManifestAttributes
|
|
39
|
+
from plm_skill_kernel.executor._demo_skills import (
|
|
40
|
+
demo_foundation_retrieve,
|
|
41
|
+
demo_foundation_synthesize,
|
|
42
|
+
load_demo_skills,
|
|
43
|
+
)
|
|
44
|
+
from plm_skill_kernel.mocks.foundation_seed import SeedFoundationKnowledgeProvider
|
|
45
|
+
|
|
46
|
+
# The 28 governed ``manifest-profile/1.0`` machine keys the fixture mirrors.
|
|
47
|
+
# Hardcoded (not derived from the model) so a drift in the model is caught
|
|
48
|
+
# against the profile's actual field inventory, not against itself.
|
|
49
|
+
_EXPECTED_MANIFEST_KEYS = frozenset(
|
|
50
|
+
{
|
|
51
|
+
"skill_id",
|
|
52
|
+
"name",
|
|
53
|
+
"mission",
|
|
54
|
+
"semantic_version",
|
|
55
|
+
"lifecycle_state",
|
|
56
|
+
"maturity_level",
|
|
57
|
+
"autonomy_ceiling",
|
|
58
|
+
"tenant_scope",
|
|
59
|
+
"audience",
|
|
60
|
+
"owner",
|
|
61
|
+
"minimum_trace_level",
|
|
62
|
+
"hitl_policy_reference",
|
|
63
|
+
"certification_reference",
|
|
64
|
+
"evaluation_reference",
|
|
65
|
+
"pinned_baseline_reference",
|
|
66
|
+
"contract_hash",
|
|
67
|
+
"input_schema_ref",
|
|
68
|
+
"output_schema_ref",
|
|
69
|
+
"preconditions",
|
|
70
|
+
"postconditions",
|
|
71
|
+
"failure_modes",
|
|
72
|
+
"evidence_expectations",
|
|
73
|
+
"kernel_api_compatibility",
|
|
74
|
+
"knowledge_api_compatibility",
|
|
75
|
+
"model_configuration",
|
|
76
|
+
"connector_deps",
|
|
77
|
+
"inference_deps",
|
|
78
|
+
"knowledge_deps",
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# The faked maturity string that must be confined to the demo fixture module.
|
|
83
|
+
_FIREWALLED_MATURITY = "certified_industrial_capability"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.fixture(autouse=True)
|
|
87
|
+
def _enable_mocks(monkeypatch):
|
|
88
|
+
"""Enable the mocks quarantine so the seed provider can be constructed."""
|
|
89
|
+
monkeypatch.setenv("PLM_MVP_MOCKS", "1")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _ctx(plan) -> SkillContext:
|
|
93
|
+
return SkillContext(
|
|
94
|
+
skill_id=plan.skill_id,
|
|
95
|
+
skill_version=plan.version,
|
|
96
|
+
tenant_id="tenant-demo",
|
|
97
|
+
autonomy_level="L1",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ── AC1: carries all manifest attributes ──────────────────────────────────
|
|
102
|
+
def test_manifest_mirrors_all_profile_keys():
|
|
103
|
+
assert set(DemoManifestAttributes.model_fields) == _EXPECTED_MANIFEST_KEYS
|
|
104
|
+
assert len(DemoManifestAttributes.model_fields) == 28
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_manifest_certified_tier_values_populated():
|
|
108
|
+
# The "certified" story: the fixture claims certified maturity and fills
|
|
109
|
+
# the certified-tier governance references (all fakes, but present).
|
|
110
|
+
assert DEMO_MANIFEST.maturity_level == _FIREWALLED_MATURITY
|
|
111
|
+
assert DEMO_MANIFEST.lifecycle_state == "released"
|
|
112
|
+
assert DEMO_MANIFEST.autonomy_ceiling == "supervised"
|
|
113
|
+
assert DEMO_MANIFEST.certification_reference.startswith("mock://certification/")
|
|
114
|
+
assert DEMO_MANIFEST.evaluation_reference.startswith("mock://evaluation/")
|
|
115
|
+
assert DEMO_MANIFEST.pinned_baseline_reference.startswith("mock://baseline/")
|
|
116
|
+
assert DEMO_MANIFEST.skill_id == DEMO_SKILL_ID
|
|
117
|
+
assert DEMO_MANIFEST.semantic_version == DEMO_SKILL_VERSION
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ── AC2: marked certified/controlled + registered in the catalog ──────────
|
|
121
|
+
def test_catalog_plan_is_certified_controlled_with_manifest():
|
|
122
|
+
plan = resolve_binding(DEMO_SKILL_ID, catalog=build_demo_catalog())
|
|
123
|
+
assert plan.certified is True
|
|
124
|
+
assert plan.controlled is True
|
|
125
|
+
assert plan.manifest is DEMO_MANIFEST
|
|
126
|
+
# An ordered set of >= 2 tasks (AC1's "ordered set of tasks").
|
|
127
|
+
assert plan.task_ids() == ("retrieve", "synthesize")
|
|
128
|
+
assert len(plan.tasks) >= 2
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# ── AC3: end-to-end request → tasks → knowledge → grounded result ─────────
|
|
132
|
+
async def test_end_to_end_agent_delegates_to_certified_composite(fresh_registry):
|
|
133
|
+
load_demo_skills() # deliberate opt-in (not boot-loaded)
|
|
134
|
+
|
|
135
|
+
request_text = "look up the foundation reference for the aluminium pump housing"
|
|
136
|
+
|
|
137
|
+
# The agent delegates: it routes to the certified composite skill id...
|
|
138
|
+
skill_id = select_skill(request_text)
|
|
139
|
+
assert skill_id == DEMO_SKILL_ID
|
|
140
|
+
|
|
141
|
+
# ...resolves it to an ordered task plan it did not itself author...
|
|
142
|
+
plan = resolve_binding(skill_id, catalog=build_demo_catalog())
|
|
143
|
+
assert plan.certified is True # delegating to a *certified* skill
|
|
144
|
+
|
|
145
|
+
result = await SkillExecutor(
|
|
146
|
+
dispatcher=InProcessDispatcher(),
|
|
147
|
+
knowledge=SeedFoundationKnowledgeProvider(top_k=8),
|
|
148
|
+
).run(plan, parse_request(request_text), context=_ctx(plan))
|
|
149
|
+
|
|
150
|
+
assert result.ok is True
|
|
151
|
+
state = result.result
|
|
152
|
+
|
|
153
|
+
# The ordered tasks ran, and foundation knowledge was injected into task 1
|
|
154
|
+
# (retrieve) but not task 2 (synthesize) — proving per-task threading.
|
|
155
|
+
trace = state["_trace"]
|
|
156
|
+
assert [e["task_id"] for e in trace] == ["retrieve", "synthesize"]
|
|
157
|
+
retrieve_entry = next(e for e in trace if e["task_id"] == "retrieve")
|
|
158
|
+
synth_entry = next(e for e in trace if e["task_id"] == "synthesize")
|
|
159
|
+
assert retrieve_entry["knowledge_injected"] is True
|
|
160
|
+
assert synth_entry["knowledge_injected"] is False
|
|
161
|
+
|
|
162
|
+
# The result is grounded and cites foundation snippet ids (FND-*).
|
|
163
|
+
answer = state["result"]
|
|
164
|
+
assert answer["grounded"] is True
|
|
165
|
+
assert answer["citations"], "expected at least one citation"
|
|
166
|
+
cited_ids = [c["id"] for c in answer["citations"]]
|
|
167
|
+
assert all(cid.startswith("FND-") for cid in cited_ids)
|
|
168
|
+
# The aluminium housing record is the natural top hit for this query.
|
|
169
|
+
assert "FND-101" in cited_ids or "FND-201" in cited_ids
|
|
170
|
+
assert all(c["source"] == "foundation-seed (MOCK)" for c in answer["citations"])
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# ── AC4: executor does NOT gate on a VALID conformance verdict ────────────
|
|
174
|
+
async def test_executor_does_not_gate_on_conformance(fresh_registry):
|
|
175
|
+
"""A fully-manifested certified plan and a bare plan run identically.
|
|
176
|
+
|
|
177
|
+
The executor never consults ``certified`` / ``controlled`` / ``manifest``
|
|
178
|
+
(the profile is non-effective — AC4). Stripping all of it must not change
|
|
179
|
+
the outcome; if the executor gated on a VALID verdict, the bare plan would
|
|
180
|
+
diverge.
|
|
181
|
+
"""
|
|
182
|
+
load_demo_skills()
|
|
183
|
+
request_text = "aluminium pump housing foundation reference"
|
|
184
|
+
catalog = build_demo_catalog()
|
|
185
|
+
certified_plan = resolve_binding(DEMO_SKILL_ID, catalog=catalog)
|
|
186
|
+
|
|
187
|
+
# Same ordered tasks, but no certification/manifest metadata at all.
|
|
188
|
+
bare_plan = certified_plan.model_copy(
|
|
189
|
+
update={"certified": False, "controlled": False, "manifest": None}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
executor = SkillExecutor(
|
|
193
|
+
dispatcher=InProcessDispatcher(),
|
|
194
|
+
knowledge=SeedFoundationKnowledgeProvider(top_k=8),
|
|
195
|
+
)
|
|
196
|
+
certified_result = await executor.run(
|
|
197
|
+
certified_plan, parse_request(request_text), context=_ctx(certified_plan)
|
|
198
|
+
)
|
|
199
|
+
bare_result = await executor.run(
|
|
200
|
+
bare_plan, parse_request(request_text), context=_ctx(bare_plan)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
assert certified_result.ok is True
|
|
204
|
+
assert bare_result.ok is True
|
|
205
|
+
# Identical grounded answer regardless of the (ignored) governance metadata.
|
|
206
|
+
assert certified_result.result["result"] == bare_result.result["result"]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ── governance firewall: the faked maturity string is confined ────────────
|
|
210
|
+
def test_firewall_certified_string_confined_to_fixture_module():
|
|
211
|
+
import plm_skill_kernel
|
|
212
|
+
|
|
213
|
+
package_root = os.path.dirname(plm_skill_kernel.__file__)
|
|
214
|
+
offenders = []
|
|
215
|
+
for dirpath, _dirs, files in os.walk(package_root):
|
|
216
|
+
for fname in files:
|
|
217
|
+
if not fname.endswith(".py"):
|
|
218
|
+
continue
|
|
219
|
+
path = os.path.join(dirpath, fname)
|
|
220
|
+
with open(path, encoding="utf-8") as fh:
|
|
221
|
+
if _FIREWALLED_MATURITY in fh.read():
|
|
222
|
+
offenders.append(os.path.relpath(path, package_root))
|
|
223
|
+
|
|
224
|
+
assert offenders == [os.path.join("executor", "_demo_manifest.py")], (
|
|
225
|
+
"the faked certified maturity string must live ONLY in the demo "
|
|
226
|
+
f"fixture module; found in: {offenders}"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# ── optional unit tests for the two composite tasks ───────────────────────
|
|
231
|
+
async def test_retrieve_narrows_injected_foundation():
|
|
232
|
+
# Two snippets injected; only the aluminium one overlaps the query tokens.
|
|
233
|
+
foundation = [
|
|
234
|
+
{
|
|
235
|
+
"id": "FND-101",
|
|
236
|
+
"type": "material_spec",
|
|
237
|
+
"title": "Aluminium 6061-T6",
|
|
238
|
+
"snippet": "aluminium housing alloy",
|
|
239
|
+
"source": "foundation-seed (MOCK)",
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"id": "FND-401",
|
|
243
|
+
"type": "unit_def",
|
|
244
|
+
"title": "Pressure units — bar and MPa",
|
|
245
|
+
"snippet": "pressure conversion",
|
|
246
|
+
"source": "foundation-seed (MOCK)",
|
|
247
|
+
},
|
|
248
|
+
]
|
|
249
|
+
out = await demo_foundation_retrieve(
|
|
250
|
+
{"query": "aluminium housing", "foundation": foundation}, ctx=None
|
|
251
|
+
)
|
|
252
|
+
assert [s["id"] for s in out["retrieved"]] == ["FND-101"]
|
|
253
|
+
assert out["match_count"] == 1
|
|
254
|
+
assert out["considered"] == 2
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
async def test_synthesize_empty_retrieval_is_honest():
|
|
258
|
+
out = await demo_foundation_synthesize({"query": "nothing", "retrieved": []}, ctx=None)
|
|
259
|
+
assert out["grounded"] is False
|
|
260
|
+
assert out["citations"] == []
|
|
261
|
+
assert "no foundation reference matched" in out["answer"]
|
|
@@ -208,7 +208,7 @@ def test_resolve_binding_exact_version():
|
|
|
208
208
|
plan = resolve_binding(DEMO_SKILL_ID, DEMO_SKILL_VERSION, catalog=catalog)
|
|
209
209
|
assert plan.skill_id == DEMO_SKILL_ID
|
|
210
210
|
assert len(plan.tasks) == 2 # ordered two-task demo skill
|
|
211
|
-
assert plan.task_ids() == ("
|
|
211
|
+
assert plan.task_ids() == ("retrieve", "synthesize")
|
|
212
212
|
|
|
213
213
|
|
|
214
214
|
def test_resolve_binding_none_version_single():
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"""Demo catalog fixture (FTR-2232 / E1).
|
|
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`. The concrete,
|
|
6
|
-
# *certified* demo skill package is story FTR-2235 / E4 — it replaces /
|
|
7
|
-
# extends this fixture with a data-dependent, human-signed-off plan. E1 ships
|
|
8
|
-
# only a structurally-valid, resolvable entry so ``resolve_binding`` has
|
|
9
|
-
# something to resolve and the seam is exercised end to end.
|
|
10
|
-
|
|
11
|
-
The demo plan below composes two already-registered V1 knowledge skills into
|
|
12
|
-
one ordered, two-task composite skill and threads a foundation-knowledge
|
|
13
|
-
lookup into the first task — enough to demonstrate the executor mechanism.
|
|
14
|
-
The ``certified`` / ``controlled`` flags are advisory demo values (present,
|
|
15
|
-
unenforced); they must never be read as real ratifications.
|
|
16
|
-
"""
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
|
-
from typing import Dict, Tuple
|
|
20
|
-
|
|
21
|
-
from .plan import SkillPlan, TaskStep
|
|
22
|
-
|
|
23
|
-
# A demo corpus id understood by the in-process knowledge seed corpus.
|
|
24
|
-
_DEMO_CORPUS = "foundation"
|
|
25
|
-
|
|
26
|
-
DEMO_SKILL_ID = "demo.knowledge_lookup"
|
|
27
|
-
DEMO_SKILL_VERSION = "0.1.0"
|
|
28
|
-
|
|
29
|
-
_DEMO_PLAN = SkillPlan(
|
|
30
|
-
skill_id=DEMO_SKILL_ID,
|
|
31
|
-
version=DEMO_SKILL_VERSION,
|
|
32
|
-
description=(
|
|
33
|
-
"Demo composite skill: search the foundation corpus, then fetch a "
|
|
34
|
-
"record — an ordered two-task skill the agent delegates to."
|
|
35
|
-
),
|
|
36
|
-
# MOCK(MVP-DEMO): fixture governance flags — not real certification.
|
|
37
|
-
certified=True,
|
|
38
|
-
controlled=True,
|
|
39
|
-
tasks=(
|
|
40
|
-
TaskStep(
|
|
41
|
-
task_id="search",
|
|
42
|
-
skill_id="knowledge.search",
|
|
43
|
-
version="1.0.0",
|
|
44
|
-
description="Ordered task 1 — semantic search over the corpus.",
|
|
45
|
-
static_payload={"corpus_id": _DEMO_CORPUS, "top_k": 5},
|
|
46
|
-
input_keys=("query",),
|
|
47
|
-
knowledge_query="foundation overview",
|
|
48
|
-
output_key="search_result",
|
|
49
|
-
),
|
|
50
|
-
TaskStep(
|
|
51
|
-
task_id="get_record",
|
|
52
|
-
skill_id="knowledge.get_record",
|
|
53
|
-
version="1.0.0",
|
|
54
|
-
description="Ordered task 2 — fetch a specific record by id.",
|
|
55
|
-
static_payload={"corpus_id": _DEMO_CORPUS},
|
|
56
|
-
input_keys=("record_id",),
|
|
57
|
-
output_key="record_result",
|
|
58
|
-
),
|
|
59
|
-
),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# Catalog is keyed by (skill_id, version); a None-version lookup resolves to
|
|
64
|
-
# the sole registered version for that id (demo convenience).
|
|
65
|
-
DEMO_CATALOG: Dict[Tuple[str, str], SkillPlan] = {
|
|
66
|
-
(DEMO_SKILL_ID, DEMO_SKILL_VERSION): _DEMO_PLAN,
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def build_demo_catalog() -> Dict[Tuple[str, str], SkillPlan]:
|
|
71
|
-
"""Return a fresh copy of the demo catalog (callers may extend it)."""
|
|
72
|
-
return dict(DEMO_CATALOG)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
__all__ = [
|
|
76
|
-
"DEMO_CATALOG",
|
|
77
|
-
"DEMO_SKILL_ID",
|
|
78
|
-
"DEMO_SKILL_VERSION",
|
|
79
|
-
"build_demo_catalog",
|
|
80
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_output_safety.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/_serialize.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/conformance/exit_codes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/engine_core_stub.py
RENAMED
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/mocks/knowledge_services_port.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/agents/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/__init__.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/dedupe.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/cleansing/normalise.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/documents/__init__.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/documents/parse.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/__init__.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/_v1_corpus.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/get_record.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/resolve.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/retrieve.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/search.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills/knowledge/validate.py
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel/skills_legacy/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/plm_skill_kernel.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plm_skill_kernel-1.3.0 → plm_skill_kernel-1.4.0}/tests/test_knowledge_services_port_mock.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|