vcp-sdk 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.
- vcp/__init__.py +50 -0
- vcp/_schema/vcp-lite-1.0.schema.json +233 -0
- vcp/context.py +290 -0
- vcp/csm1.py +418 -0
- vcp/lite.py +203 -0
- vcp/token.py +289 -0
- vcp_sdk-0.3.0.dist-info/METADATA +95 -0
- vcp_sdk-0.3.0.dist-info/RECORD +9 -0
- vcp_sdk-0.3.0.dist-info/WHEEL +4 -0
vcp/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""VCP SDK — Value Context Protocol for portable AI ethics.
|
|
2
|
+
|
|
3
|
+
Validate tokens, parse CSM1 codes, and work with VCP-Lite definitions.
|
|
4
|
+
|
|
5
|
+
>>> from vcp import Token, CSM1Code, validate_lite
|
|
6
|
+
>>> token = Token.parse("family.safe.guide@1.0.0")
|
|
7
|
+
>>> code = CSM1Code.parse("N5+F+E")
|
|
8
|
+
>>> errors = validate_lite({"vcp_version": "lite-1.0", ...})
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .context import Context
|
|
12
|
+
from .csm1 import (
|
|
13
|
+
ADHERENCE_BEHAVIORS,
|
|
14
|
+
PERSONA_NAMES,
|
|
15
|
+
SCOPE_CONFLICTS,
|
|
16
|
+
SCOPE_SYNERGIES,
|
|
17
|
+
V2_SCOPE_CHARS,
|
|
18
|
+
CSM1Code,
|
|
19
|
+
Persona,
|
|
20
|
+
Scope,
|
|
21
|
+
check_scope_conflicts,
|
|
22
|
+
)
|
|
23
|
+
from .lite import lite_to_csm1, lite_to_token, validate_lite
|
|
24
|
+
from .token import Token, canonicalize_token, tokens_equal, uri_to_canonical
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Context (v0.2.0)
|
|
28
|
+
"Context",
|
|
29
|
+
# Token
|
|
30
|
+
"Token",
|
|
31
|
+
"canonicalize_token",
|
|
32
|
+
"tokens_equal",
|
|
33
|
+
"uri_to_canonical",
|
|
34
|
+
# CSM1
|
|
35
|
+
"CSM1Code",
|
|
36
|
+
"Persona",
|
|
37
|
+
"Scope",
|
|
38
|
+
"PERSONA_NAMES",
|
|
39
|
+
"V2_SCOPE_CHARS",
|
|
40
|
+
"SCOPE_CONFLICTS",
|
|
41
|
+
"SCOPE_SYNERGIES",
|
|
42
|
+
"ADHERENCE_BEHAVIORS",
|
|
43
|
+
"check_scope_conflicts",
|
|
44
|
+
# Lite
|
|
45
|
+
"validate_lite",
|
|
46
|
+
"lite_to_csm1",
|
|
47
|
+
"lite_to_token",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://vcp.creed.space/schema/vcp-lite/v1.json",
|
|
4
|
+
"title": "VCP-Lite Agent Values",
|
|
5
|
+
"description": "Minimal portable values definition for AI agents. A simplified entry point to the Value Context Protocol (VCP).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"vcp_version": {
|
|
9
|
+
"const": "lite-1.0",
|
|
10
|
+
"description": "Schema version identifier"
|
|
11
|
+
},
|
|
12
|
+
"identity": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"description": "Agent ethical identity as a hierarchical token. Maps to VCP/I token format (domain.approach.role).",
|
|
15
|
+
"properties": {
|
|
16
|
+
"domain": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
19
|
+
"maxLength": 32,
|
|
20
|
+
"description": "Top-level domain (e.g. family, company, org, community)"
|
|
21
|
+
},
|
|
22
|
+
"approach": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
25
|
+
"maxLength": 32,
|
|
26
|
+
"description": "Ethical approach or methodology (e.g. safe, strict, open)"
|
|
27
|
+
},
|
|
28
|
+
"role": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
31
|
+
"maxLength": 32,
|
|
32
|
+
"description": "Functional role (e.g. guide, guardian, advisor, creator)"
|
|
33
|
+
},
|
|
34
|
+
"version": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Optional semantic version or alias",
|
|
37
|
+
"oneOf": [
|
|
38
|
+
{"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
|
|
39
|
+
{"enum": ["latest", "canary"]}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"required": ["domain", "approach", "role"],
|
|
44
|
+
"additionalProperties": false
|
|
45
|
+
},
|
|
46
|
+
"persona": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["nanny", "sentinel", "godparent", "ambassador", "muse", "mediator", "custom"],
|
|
49
|
+
"description": "Archetypal persona governing behavioral stance. Maps to VCP/S CSM1 persona code."
|
|
50
|
+
},
|
|
51
|
+
"adherence": {
|
|
52
|
+
"type": "integer",
|
|
53
|
+
"minimum": 0,
|
|
54
|
+
"maximum": 5,
|
|
55
|
+
"description": "Enforcement level (0=advisory, 1=soft, 2=moderate, 3=active, 4=strict, 5=absolute). Maps to VCP/S adherence level."
|
|
56
|
+
},
|
|
57
|
+
"scopes": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"enum": ["F", "W", "P", "E", "T", "O", "V", "A", "H", "S", "R"]
|
|
62
|
+
},
|
|
63
|
+
"minItems": 1,
|
|
64
|
+
"uniqueItems": true,
|
|
65
|
+
"description": "Context scopes where this agent operates. Maps to VCP/S scope codes."
|
|
66
|
+
},
|
|
67
|
+
"values": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"items": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"principle": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"maxLength": 500,
|
|
75
|
+
"description": "Human-readable ethical principle"
|
|
76
|
+
},
|
|
77
|
+
"weight": {
|
|
78
|
+
"type": "number",
|
|
79
|
+
"minimum": 0.0,
|
|
80
|
+
"maximum": 1.0,
|
|
81
|
+
"description": "Relative importance (0.0=informational, 1.0=critical)"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"required": ["principle", "weight"],
|
|
85
|
+
"additionalProperties": false
|
|
86
|
+
},
|
|
87
|
+
"maxItems": 20,
|
|
88
|
+
"description": "Ordered ethical principles with relative weights"
|
|
89
|
+
},
|
|
90
|
+
"constraints": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"maxLength": 500
|
|
95
|
+
},
|
|
96
|
+
"maxItems": 20,
|
|
97
|
+
"description": "Hard boundaries that override all other behavior. These are non-negotiable."
|
|
98
|
+
},
|
|
99
|
+
"adaptation_hints": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"description": "Simplified context preferences. For full contextual adaptation, upgrade to VCP/A.",
|
|
102
|
+
"properties": {
|
|
103
|
+
"formality": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"enum": ["casual", "neutral", "formal"],
|
|
106
|
+
"description": "Communication register"
|
|
107
|
+
},
|
|
108
|
+
"sensitivity": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"enum": ["low", "moderate", "high"],
|
|
111
|
+
"description": "Content sensitivity level"
|
|
112
|
+
},
|
|
113
|
+
"autonomy": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"enum": ["autonomous", "guided", "supervised"],
|
|
116
|
+
"description": "Agent decision-making independence"
|
|
117
|
+
},
|
|
118
|
+
"transparency": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"enum": ["minimal", "standard", "full"],
|
|
121
|
+
"description": "How much reasoning the agent exposes"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"additionalProperties": false
|
|
125
|
+
},
|
|
126
|
+
"namespace": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"pattern": "^[A-Z][A-Z0-9]{0,7}$",
|
|
129
|
+
"description": "Optional organizational namespace (1-8 uppercase chars). Required when persona is 'custom'."
|
|
130
|
+
},
|
|
131
|
+
"metadata": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"description": "Optional provenance and licensing information",
|
|
134
|
+
"properties": {
|
|
135
|
+
"author": {
|
|
136
|
+
"type": "string",
|
|
137
|
+
"description": "Author or organization"
|
|
138
|
+
},
|
|
139
|
+
"license": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"description": "SPDX license identifier (e.g. CC-BY-4.0, Apache-2.0)"
|
|
142
|
+
},
|
|
143
|
+
"created": {
|
|
144
|
+
"type": "string",
|
|
145
|
+
"format": "date",
|
|
146
|
+
"description": "Creation date (ISO 8601)"
|
|
147
|
+
},
|
|
148
|
+
"description": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"maxLength": 1000,
|
|
151
|
+
"description": "Human-readable description of this values profile"
|
|
152
|
+
},
|
|
153
|
+
"extends": {
|
|
154
|
+
"type": "string",
|
|
155
|
+
"description": "VCP token of a parent profile this extends (e.g. family.safe.guide@1.0.0)"
|
|
156
|
+
},
|
|
157
|
+
"tags": {
|
|
158
|
+
"type": "array",
|
|
159
|
+
"items": {"type": "string"},
|
|
160
|
+
"maxItems": 10,
|
|
161
|
+
"description": "Searchable tags"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"additionalProperties": false
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"required": ["vcp_version", "identity", "persona", "adherence", "scopes"],
|
|
168
|
+
"additionalProperties": false,
|
|
169
|
+
"if": {
|
|
170
|
+
"properties": {"persona": {"const": "custom"}}
|
|
171
|
+
},
|
|
172
|
+
"then": {
|
|
173
|
+
"required": ["vcp_version", "identity", "persona", "adherence", "scopes", "namespace"]
|
|
174
|
+
},
|
|
175
|
+
"$defs": {
|
|
176
|
+
"scope_descriptions": {
|
|
177
|
+
"description": "Reference: VCP/S v2.0 scope code meanings",
|
|
178
|
+
"type": "object",
|
|
179
|
+
"properties": {
|
|
180
|
+
"F": {"const": "Family-appropriate, child-safe"},
|
|
181
|
+
"W": {"const": "Professional workplace"},
|
|
182
|
+
"P": {"const": "Privacy-focused, data protection"},
|
|
183
|
+
"E": {"const": "Educational context"},
|
|
184
|
+
"T": {"const": "Developer/technical context"},
|
|
185
|
+
"O": {"const": "Official/governmental"},
|
|
186
|
+
"V": {"const": "Vulnerable populations"},
|
|
187
|
+
"A": {"const": "Adult-only, explicit allowed"},
|
|
188
|
+
"H": {"const": "Healthcare/medical"},
|
|
189
|
+
"S": {"const": "Social media/community"},
|
|
190
|
+
"R": {"const": "Religious/spiritual"}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"adherence_descriptions": {
|
|
194
|
+
"description": "Reference: VCP/S v2.0 adherence level behaviors",
|
|
195
|
+
"type": "object",
|
|
196
|
+
"properties": {
|
|
197
|
+
"0": {"const": "Advisory: informational only, user always overrides"},
|
|
198
|
+
"1": {"const": "Soft: gentle warnings, user acknowledges to override"},
|
|
199
|
+
"2": {"const": "Moderate: clear warnings, user provides reason to override"},
|
|
200
|
+
"3": {"const": "Active: prominent warnings, limited override scenarios"},
|
|
201
|
+
"4": {"const": "Strict: explicit warnings, override only in exceptional cases"},
|
|
202
|
+
"5": {"const": "Absolute: blocking, no user override permitted"}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
"persona_descriptions": {
|
|
206
|
+
"description": "Reference: VCP/S v2.0 persona archetypes",
|
|
207
|
+
"type": "object",
|
|
208
|
+
"properties": {
|
|
209
|
+
"nanny": {"const": "Child safety and family-appropriate content (code: N)"},
|
|
210
|
+
"sentinel": {"const": "Security, privacy, and operational safety (code: Z)"},
|
|
211
|
+
"godparent": {"const": "Ethical guidance and moral reasoning (code: G)"},
|
|
212
|
+
"ambassador": {"const": "Professional conduct and diplomatic communication (code: A)"},
|
|
213
|
+
"muse": {"const": "Creativity and artistic expression (code: M)"},
|
|
214
|
+
"mediator": {"const": "Fair resolution and balanced mediation (code: D)"},
|
|
215
|
+
"custom": {"const": "User-defined constitution, requires namespace (code: C)"}
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"scope_conflicts": {
|
|
219
|
+
"description": "Reference: Mutually exclusive scope pairs from VCP/S v2.0",
|
|
220
|
+
"type": "array",
|
|
221
|
+
"items": {
|
|
222
|
+
"type": "array",
|
|
223
|
+
"items": {"type": "string"},
|
|
224
|
+
"minItems": 2,
|
|
225
|
+
"maxItems": 2
|
|
226
|
+
},
|
|
227
|
+
"const": [
|
|
228
|
+
["F", "A"],
|
|
229
|
+
["V", "A"]
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
vcp/context.py
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""VCP Context encoding and decoding.
|
|
2
|
+
|
|
3
|
+
Encode and decode the 18 VCP context dimensions (13 situational + 5 personal)
|
|
4
|
+
for portable context propagation across AI agent sessions and platforms.
|
|
5
|
+
|
|
6
|
+
Per VEP-0004 (2026-04-17), four new situational dimensions were added to the
|
|
7
|
+
canonical 9: EMBODIMENT, PROXIMITY, RELATIONSHIP, and FORMALITY. Extended support
|
|
8
|
+
is advertised via capability token ``vcp-a-ext-v1``.
|
|
9
|
+
|
|
10
|
+
>>> from vcp import Context
|
|
11
|
+
>>> ctx = Context(space="hospital", agency="peer", constraints=["legal"])
|
|
12
|
+
>>> ctx.to_wire()
|
|
13
|
+
'📍hospital|🎯peer|🔒legal'
|
|
14
|
+
>>> ctx.to_dict()
|
|
15
|
+
{'space': 'hospital', 'agency': 'peer', 'constraints': ['legal'], 'version': '3.2'}
|
|
16
|
+
|
|
17
|
+
>>> # Embodied-AI example (VEP-0004)
|
|
18
|
+
>>> ctx = Context(
|
|
19
|
+
... space="hospital",
|
|
20
|
+
... embodiment="manipulating",
|
|
21
|
+
... proximity="close",
|
|
22
|
+
... relationship="colleague:professional",
|
|
23
|
+
... formality="professional",
|
|
24
|
+
... )
|
|
25
|
+
|
|
26
|
+
For Managed Agents integration:
|
|
27
|
+
>>> metadata = ctx.to_session_metadata()
|
|
28
|
+
>>> restored = Context.from_session_metadata(metadata)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import json
|
|
34
|
+
from dataclasses import dataclass
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
# Emoji symbols for wire format
|
|
38
|
+
# Canonical 9 situational dimensions (VCP/A v2.0)
|
|
39
|
+
# Plus VEP-0004 dimensions: EMBODIMENT, PROXIMITY, RELATIONSHIP, FORMALITY
|
|
40
|
+
_SITUATIONAL_SYMBOLS: dict[str, str] = {
|
|
41
|
+
"time": "\U0001f550", # clock
|
|
42
|
+
"space": "\U0001f4cd", # pin
|
|
43
|
+
"company": "\U0001f465", # people
|
|
44
|
+
"culture": "\U0001f30d", # globe
|
|
45
|
+
"occasion": "\U0001f4c5", # calendar
|
|
46
|
+
"environment": "\U0001f324", # sun
|
|
47
|
+
"agency": "\U0001f3af", # target
|
|
48
|
+
"constraints": "\U0001f512", # lock
|
|
49
|
+
"system_context": "\U0001f4e1", # satellite antenna
|
|
50
|
+
# VEP-0004 extended dimensions
|
|
51
|
+
"embodiment": "\U0001f9cd", # 🧍 person standing
|
|
52
|
+
"proximity": "\u2194\ufe0f", # ↔️ left-right arrow
|
|
53
|
+
"relationship": "\U0001faa2", # 🪢 knot
|
|
54
|
+
"formality": "\U0001f3a9", # 🎩 top hat
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_PERSONAL_SYMBOLS: dict[str, str] = {
|
|
58
|
+
"cognitive_state": "\U0001f9e0", # brain
|
|
59
|
+
"emotional_tone": "\U0001f4ad", # thought
|
|
60
|
+
"energy_level": "\U0001f50b", # battery
|
|
61
|
+
"perceived_urgency": "\u26a1", # lightning
|
|
62
|
+
"body_signals": "\U0001fa7a", # stethoscope
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
SITUATIONAL_DIMENSIONS = (
|
|
66
|
+
"time",
|
|
67
|
+
"space",
|
|
68
|
+
"company",
|
|
69
|
+
"culture",
|
|
70
|
+
"occasion",
|
|
71
|
+
"environment",
|
|
72
|
+
"agency",
|
|
73
|
+
"constraints",
|
|
74
|
+
"system_context",
|
|
75
|
+
# VEP-0004 extended dimensions (order fixed per spec §2.5)
|
|
76
|
+
"embodiment",
|
|
77
|
+
"proximity",
|
|
78
|
+
"relationship",
|
|
79
|
+
"formality",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
PERSONAL_DIMENSIONS = (
|
|
83
|
+
"cognitive_state",
|
|
84
|
+
"emotional_tone",
|
|
85
|
+
"energy_level",
|
|
86
|
+
"perceived_urgency",
|
|
87
|
+
"body_signals",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# VEP-0004 canonical value vocabularies (for validation helpers)
|
|
91
|
+
EMBODIMENT_VALUES = (
|
|
92
|
+
"stationary",
|
|
93
|
+
"navigating",
|
|
94
|
+
"manipulating",
|
|
95
|
+
"carrying",
|
|
96
|
+
"emergency_stop",
|
|
97
|
+
)
|
|
98
|
+
PROXIMITY_VALUES = (
|
|
99
|
+
"distant",
|
|
100
|
+
"same_room",
|
|
101
|
+
"nearby",
|
|
102
|
+
"close",
|
|
103
|
+
"contact",
|
|
104
|
+
)
|
|
105
|
+
FORMALITY_VALUES = (
|
|
106
|
+
"casual",
|
|
107
|
+
"professional",
|
|
108
|
+
"formal",
|
|
109
|
+
"ceremonial",
|
|
110
|
+
)
|
|
111
|
+
# RELATIONSHIP is compound "{tie}:{function}"; components below
|
|
112
|
+
RELATIONSHIP_TIES = (
|
|
113
|
+
"stranger",
|
|
114
|
+
"acquaintance",
|
|
115
|
+
"colleague",
|
|
116
|
+
"friend",
|
|
117
|
+
"family",
|
|
118
|
+
"intimate",
|
|
119
|
+
"long_term",
|
|
120
|
+
"trusted_collaborator",
|
|
121
|
+
)
|
|
122
|
+
RELATIONSHIP_FUNCTIONS = (
|
|
123
|
+
"transactional",
|
|
124
|
+
"professional",
|
|
125
|
+
"educational",
|
|
126
|
+
"therapeutic",
|
|
127
|
+
"social",
|
|
128
|
+
"intimate",
|
|
129
|
+
"adversarial",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class Context:
|
|
135
|
+
"""VCP 3.2 context with 18 dimensions (VEP-0004).
|
|
136
|
+
|
|
137
|
+
Situational (13): time, space, company, culture, occasion, environment,
|
|
138
|
+
agency, constraints, system_context, embodiment, proximity,
|
|
139
|
+
relationship, formality
|
|
140
|
+
Personal (5): cognitive_state, emotional_tone, energy_level,
|
|
141
|
+
perceived_urgency, body_signals (each with optional 1-5 intensity)
|
|
142
|
+
|
|
143
|
+
VEP-0004 dimensions (embodiment, proximity, relationship, formality) are
|
|
144
|
+
optional. Implementations MAY omit them from wire encodings when at default
|
|
145
|
+
or unknown. Receivers MUST treat absent dimensions as "no information".
|
|
146
|
+
Advertise support via capability token ``vcp-a-ext-v1`` (see VEP-0002).
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
# Canonical situational (9)
|
|
150
|
+
time: str | None = None
|
|
151
|
+
space: str | None = None
|
|
152
|
+
company: str | list[str] | None = None
|
|
153
|
+
culture: str | None = None
|
|
154
|
+
occasion: str | None = None
|
|
155
|
+
environment: str | None = None
|
|
156
|
+
agency: str | None = None
|
|
157
|
+
constraints: str | list[str] | None = None
|
|
158
|
+
system_context: str | None = None
|
|
159
|
+
|
|
160
|
+
# VEP-0004 extended situational (4)
|
|
161
|
+
embodiment: str | None = None
|
|
162
|
+
proximity: str | None = None
|
|
163
|
+
relationship: str | None = None # Compound "{tie}:{function}"
|
|
164
|
+
formality: str | None = None
|
|
165
|
+
|
|
166
|
+
# Personal (category + optional intensity)
|
|
167
|
+
cognitive_state: str | None = None
|
|
168
|
+
cognitive_state_intensity: int | None = None
|
|
169
|
+
emotional_tone: str | None = None
|
|
170
|
+
emotional_tone_intensity: int | None = None
|
|
171
|
+
energy_level: str | None = None
|
|
172
|
+
energy_level_intensity: int | None = None
|
|
173
|
+
perceived_urgency: str | None = None
|
|
174
|
+
perceived_urgency_intensity: int | None = None
|
|
175
|
+
body_signals: str | None = None
|
|
176
|
+
body_signals_intensity: int | None = None
|
|
177
|
+
|
|
178
|
+
version: str = "3.2"
|
|
179
|
+
|
|
180
|
+
def to_dict(self) -> dict[str, Any]:
|
|
181
|
+
"""Serialize to dict, omitting None values."""
|
|
182
|
+
result: dict[str, Any] = {}
|
|
183
|
+
for dim in SITUATIONAL_DIMENSIONS:
|
|
184
|
+
val = getattr(self, dim, None)
|
|
185
|
+
if val is not None:
|
|
186
|
+
result[dim] = val
|
|
187
|
+
for dim in PERSONAL_DIMENSIONS:
|
|
188
|
+
val = getattr(self, dim, None)
|
|
189
|
+
if val is not None:
|
|
190
|
+
entry: dict[str, Any] = {"category": val}
|
|
191
|
+
intensity = getattr(self, f"{dim}_intensity", None)
|
|
192
|
+
if intensity is not None:
|
|
193
|
+
entry["intensity"] = intensity
|
|
194
|
+
result[dim] = entry
|
|
195
|
+
result["version"] = self.version
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
def to_wire(self) -> str:
|
|
199
|
+
"""Encode to compact wire format (emoji-based).
|
|
200
|
+
|
|
201
|
+
Situational dims separated by ``|``, personal after ``||``.
|
|
202
|
+
"""
|
|
203
|
+
parts: list[str] = []
|
|
204
|
+
for dim, symbol in _SITUATIONAL_SYMBOLS.items():
|
|
205
|
+
val = getattr(self, dim, None)
|
|
206
|
+
if val is not None:
|
|
207
|
+
if isinstance(val, list):
|
|
208
|
+
val = "+".join(val)
|
|
209
|
+
parts.append(f"{symbol}{val}")
|
|
210
|
+
wire = "|".join(parts) if parts else ""
|
|
211
|
+
|
|
212
|
+
personal_parts: list[str] = []
|
|
213
|
+
for dim, symbol in _PERSONAL_SYMBOLS.items():
|
|
214
|
+
val = getattr(self, dim, None)
|
|
215
|
+
if val is not None:
|
|
216
|
+
intensity = getattr(self, f"{dim}_intensity", None)
|
|
217
|
+
suffix = str(intensity) if intensity else ""
|
|
218
|
+
personal_parts.append(f"{symbol}{val}{suffix}")
|
|
219
|
+
if personal_parts:
|
|
220
|
+
personal = "".join(personal_parts)
|
|
221
|
+
wire = f"{wire}||{personal}" if wire else personal
|
|
222
|
+
return wire
|
|
223
|
+
|
|
224
|
+
def to_session_metadata(self) -> dict[str, str]:
|
|
225
|
+
"""Encode for AI agent session metadata (string key-value pairs)."""
|
|
226
|
+
return {
|
|
227
|
+
"vcp_wire": self.to_wire(),
|
|
228
|
+
"vcp_version": self.version,
|
|
229
|
+
"vcp_context": json.dumps(self.to_dict()),
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def from_dict(cls, data: dict[str, Any]) -> Context:
|
|
234
|
+
"""Reconstruct from a serialized dict."""
|
|
235
|
+
kwargs: dict[str, Any] = {}
|
|
236
|
+
for dim in SITUATIONAL_DIMENSIONS:
|
|
237
|
+
if dim in data:
|
|
238
|
+
kwargs[dim] = data[dim]
|
|
239
|
+
for dim in PERSONAL_DIMENSIONS:
|
|
240
|
+
if dim in data:
|
|
241
|
+
val = data[dim]
|
|
242
|
+
if isinstance(val, dict):
|
|
243
|
+
kwargs[dim] = val.get("category")
|
|
244
|
+
if "intensity" in val:
|
|
245
|
+
kwargs[f"{dim}_intensity"] = val["intensity"]
|
|
246
|
+
elif isinstance(val, str):
|
|
247
|
+
kwargs[dim] = val
|
|
248
|
+
kwargs["version"] = data.get("version", "3.2")
|
|
249
|
+
return cls(**kwargs)
|
|
250
|
+
|
|
251
|
+
@classmethod
|
|
252
|
+
def from_session_metadata(cls, metadata: dict[str, str]) -> Context | None:
|
|
253
|
+
"""Reconstruct from AI agent session metadata. Returns None if absent."""
|
|
254
|
+
raw = metadata.get("vcp_context")
|
|
255
|
+
if not raw:
|
|
256
|
+
return None
|
|
257
|
+
try:
|
|
258
|
+
data = json.loads(raw)
|
|
259
|
+
except (json.JSONDecodeError, TypeError):
|
|
260
|
+
return None
|
|
261
|
+
return cls.from_dict(data)
|
|
262
|
+
|
|
263
|
+
def to_natural_language(self) -> str:
|
|
264
|
+
"""Convert to a human-readable context description."""
|
|
265
|
+
lines: list[str] = []
|
|
266
|
+
if self.space:
|
|
267
|
+
lines.append(f"Setting: {self.space}")
|
|
268
|
+
if self.agency:
|
|
269
|
+
lines.append(f"Role: {self.agency}")
|
|
270
|
+
if self.occasion:
|
|
271
|
+
lines.append(f"Occasion: {self.occasion}")
|
|
272
|
+
if self.constraints:
|
|
273
|
+
c = self.constraints if isinstance(self.constraints, str) else ", ".join(self.constraints)
|
|
274
|
+
lines.append(f"Constraints: {c}")
|
|
275
|
+
if self.company:
|
|
276
|
+
c = self.company if isinstance(self.company, str) else ", ".join(self.company)
|
|
277
|
+
lines.append(f"Present: {c}")
|
|
278
|
+
if self.relationship:
|
|
279
|
+
lines.append(f"Relationship: {self.relationship}")
|
|
280
|
+
if self.formality:
|
|
281
|
+
lines.append(f"Register: {self.formality}")
|
|
282
|
+
if self.embodiment and self.embodiment != "stationary":
|
|
283
|
+
lines.append(f"Motor state: {self.embodiment}")
|
|
284
|
+
if self.proximity:
|
|
285
|
+
lines.append(f"Proximity: {self.proximity}")
|
|
286
|
+
if self.perceived_urgency:
|
|
287
|
+
lines.append(f"Urgency: {self.perceived_urgency}")
|
|
288
|
+
if self.cognitive_state:
|
|
289
|
+
lines.append(f"State: {self.cognitive_state}")
|
|
290
|
+
return "; ".join(lines) if lines else ""
|