token-runtime 0.1.0a2__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.
- token_runtime/__init__.py +1 -0
- token_runtime/adapters.py +181 -0
- token_runtime/agent_integrations.py +114 -0
- token_runtime/anthropic_adapter.py +301 -0
- token_runtime/anthropic_conformance.py +336 -0
- token_runtime/anthropic_preserved_thinking_cert.py +213 -0
- token_runtime/benchmark.py +106 -0
- token_runtime/capabilities.py +111 -0
- token_runtime/cli.py +284 -0
- token_runtime/codex_recertification.py +115 -0
- token_runtime/compat_cert.py +462 -0
- token_runtime/compatibility.py +81 -0
- token_runtime/config.py +72 -0
- token_runtime/conformance.py +36 -0
- token_runtime/contracts.py +70 -0
- token_runtime/engine.py +136 -0
- token_runtime/feature_flags.py +78 -0
- token_runtime/gateway.py +151 -0
- token_runtime/gemini_adapter.py +260 -0
- token_runtime/gemini_conformance.py +351 -0
- token_runtime/integrations.py +272 -0
- token_runtime/metrics.py +79 -0
- token_runtime/model.py +39 -0
- token_runtime/openai_certification.py +310 -0
- token_runtime/planner.py +96 -0
- token_runtime/reducers.py +179 -0
- token_runtime/store.py +36 -0
- token_runtime/strategies.py +50 -0
- token_runtime/terminal_ui.py +96 -0
- token_runtime-0.1.0a2.dist-info/METADATA +238 -0
- token_runtime-0.1.0a2.dist-info/RECORD +35 -0
- token_runtime-0.1.0a2.dist-info/WHEEL +5 -0
- token_runtime-0.1.0a2.dist-info/entry_points.txt +2 -0
- token_runtime-0.1.0a2.dist-info/licenses/LICENSE +202 -0
- token_runtime-0.1.0a2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""TOKEN adaptive context optimization runtime."""
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any, Mapping, MutableMapping, Sequence
|
|
6
|
+
|
|
7
|
+
from .model import ContextBlock, RequestEnvelope
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _set_path(root: Any, path: Sequence[Any], value: str) -> None:
|
|
11
|
+
target = root
|
|
12
|
+
for key in path[:-1]:
|
|
13
|
+
target = target[key]
|
|
14
|
+
target[path[-1]] = value
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _kind_for_role(role: str) -> str:
|
|
18
|
+
if role in {"system", "developer", "user"}:
|
|
19
|
+
return role
|
|
20
|
+
if role == "tool":
|
|
21
|
+
return "tool_output"
|
|
22
|
+
return "assistant"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _block(block_id: str, kind: str, text: str, *, role=None, turn=0, path=None) -> ContextBlock:
|
|
26
|
+
metadata = {} if path is None else {"path": tuple(path)}
|
|
27
|
+
return ContextBlock(block_id, kind, text, role=role, turn_index=turn, metadata=metadata)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _AdapterBase:
|
|
31
|
+
name = "base"
|
|
32
|
+
|
|
33
|
+
def serialize(self, envelope: RequestEnvelope) -> dict[str, Any]:
|
|
34
|
+
payload = deepcopy(envelope.opaque["original_payload"])
|
|
35
|
+
for block in envelope.blocks:
|
|
36
|
+
path = block.metadata.get("path")
|
|
37
|
+
if path:
|
|
38
|
+
_set_path(payload, path, block.text)
|
|
39
|
+
return payload
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def _tool_blocks(tools: Any) -> list[ContextBlock]:
|
|
43
|
+
if not isinstance(tools, list):
|
|
44
|
+
return []
|
|
45
|
+
return [
|
|
46
|
+
_block(f"tool-schema-{i}", "tool_schema", json.dumps(tool, sort_keys=True, separators=(",", ":")))
|
|
47
|
+
for i, tool in enumerate(tools)
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ResponsesAdapter(_AdapterBase):
|
|
52
|
+
name = "responses"
|
|
53
|
+
|
|
54
|
+
def parse(self, payload: Mapping[str, Any]) -> RequestEnvelope:
|
|
55
|
+
original = deepcopy(dict(payload))
|
|
56
|
+
blocks: list[ContextBlock] = []
|
|
57
|
+
safe = True
|
|
58
|
+
instructions = payload.get("instructions")
|
|
59
|
+
if isinstance(instructions, str):
|
|
60
|
+
blocks.append(_block("instructions", "system", instructions, role="system", path=("instructions",)))
|
|
61
|
+
elif instructions is not None:
|
|
62
|
+
safe = False
|
|
63
|
+
|
|
64
|
+
blocks.extend(self._tool_blocks(payload.get("tools")))
|
|
65
|
+
input_value = payload.get("input")
|
|
66
|
+
if isinstance(input_value, str):
|
|
67
|
+
blocks.append(_block("input", "user", input_value, role="user", path=("input",)))
|
|
68
|
+
elif isinstance(input_value, list):
|
|
69
|
+
for i, item in enumerate(input_value):
|
|
70
|
+
item_blocks, item_safe = self._parse_input_item(i, item)
|
|
71
|
+
blocks.extend(item_blocks)
|
|
72
|
+
safe = safe and item_safe
|
|
73
|
+
elif input_value is not None:
|
|
74
|
+
safe = False
|
|
75
|
+
|
|
76
|
+
return RequestEnvelope(
|
|
77
|
+
blocks=tuple(blocks),
|
|
78
|
+
opaque={"original_payload": original},
|
|
79
|
+
adapter=self.name,
|
|
80
|
+
wire_safe=safe,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def _parse_input_item(self, index: int, item: Any) -> tuple[list[ContextBlock], bool]:
|
|
84
|
+
if not isinstance(item, Mapping):
|
|
85
|
+
return [], False
|
|
86
|
+
item_type = item.get("type")
|
|
87
|
+
if item_type == "function_call_output":
|
|
88
|
+
output = item.get("output")
|
|
89
|
+
if not isinstance(output, str):
|
|
90
|
+
return [], False
|
|
91
|
+
return [_block(f"input-{index}", "tool_output", output, role="tool", turn=index, path=("input", index, "output"))], True
|
|
92
|
+
if item_type == "function_call":
|
|
93
|
+
return [_block(f"input-{index}", "tool_call", json.dumps(dict(item), sort_keys=True))], True
|
|
94
|
+
|
|
95
|
+
role = item.get("role")
|
|
96
|
+
if not isinstance(role, str):
|
|
97
|
+
return [], False
|
|
98
|
+
content = item.get("content")
|
|
99
|
+
kind = _kind_for_role(role)
|
|
100
|
+
if isinstance(content, str):
|
|
101
|
+
return [
|
|
102
|
+
_block(f"input-{index}", kind, content, role=role, turn=index, path=("input", index, "content"))
|
|
103
|
+
], True
|
|
104
|
+
if isinstance(content, list):
|
|
105
|
+
blocks: list[ContextBlock] = []
|
|
106
|
+
safe = True
|
|
107
|
+
for j, part in enumerate(content):
|
|
108
|
+
if not isinstance(part, Mapping):
|
|
109
|
+
safe = False
|
|
110
|
+
continue
|
|
111
|
+
part_type = part.get("type")
|
|
112
|
+
text = part.get("text")
|
|
113
|
+
if part_type not in {"input_text", "output_text", "text"} or not isinstance(text, str):
|
|
114
|
+
safe = False
|
|
115
|
+
continue
|
|
116
|
+
blocks.append(
|
|
117
|
+
_block(
|
|
118
|
+
f"input-{index}-content-{j}",
|
|
119
|
+
kind,
|
|
120
|
+
text,
|
|
121
|
+
role=role,
|
|
122
|
+
turn=index,
|
|
123
|
+
path=("input", index, "content", j, "text"),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
return blocks, safe
|
|
127
|
+
return [], content is None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class ChatCompletionsAdapter(_AdapterBase):
|
|
131
|
+
name = "chat_completions"
|
|
132
|
+
|
|
133
|
+
def parse(self, payload: Mapping[str, Any]) -> RequestEnvelope:
|
|
134
|
+
original = deepcopy(dict(payload))
|
|
135
|
+
blocks: list[ContextBlock] = self._tool_blocks(payload.get("tools"))
|
|
136
|
+
safe = True
|
|
137
|
+
messages = payload.get("messages")
|
|
138
|
+
if not isinstance(messages, list):
|
|
139
|
+
safe = False
|
|
140
|
+
messages = []
|
|
141
|
+
|
|
142
|
+
for i, message in enumerate(messages):
|
|
143
|
+
if not isinstance(message, Mapping):
|
|
144
|
+
safe = False
|
|
145
|
+
continue
|
|
146
|
+
role = message.get("role")
|
|
147
|
+
if not isinstance(role, str):
|
|
148
|
+
safe = False
|
|
149
|
+
continue
|
|
150
|
+
kind = _kind_for_role(role)
|
|
151
|
+
content = message.get("content")
|
|
152
|
+
if isinstance(content, str):
|
|
153
|
+
blocks.append(
|
|
154
|
+
_block(f"message-{i}", kind, content, role=role, turn=i, path=("messages", i, "content"))
|
|
155
|
+
)
|
|
156
|
+
elif isinstance(content, list):
|
|
157
|
+
for j, part in enumerate(content):
|
|
158
|
+
if not isinstance(part, Mapping) or part.get("type") != "text" or not isinstance(part.get("text"), str):
|
|
159
|
+
safe = False
|
|
160
|
+
continue
|
|
161
|
+
blocks.append(
|
|
162
|
+
_block(
|
|
163
|
+
f"message-{i}-content-{j}", kind, part["text"], role=role, turn=i,
|
|
164
|
+
path=("messages", i, "content", j, "text"),
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
elif content is not None:
|
|
168
|
+
safe = False
|
|
169
|
+
tool_calls = message.get("tool_calls")
|
|
170
|
+
if tool_calls is not None:
|
|
171
|
+
if not isinstance(tool_calls, list):
|
|
172
|
+
safe = False
|
|
173
|
+
else:
|
|
174
|
+
blocks.append(_block(f"message-{i}-tool-calls", "tool_call", json.dumps(tool_calls, sort_keys=True), role=role, turn=i))
|
|
175
|
+
|
|
176
|
+
return RequestEnvelope(
|
|
177
|
+
blocks=tuple(blocks),
|
|
178
|
+
opaque={"original_payload": original},
|
|
179
|
+
adapter=self.name,
|
|
180
|
+
wire_safe=safe,
|
|
181
|
+
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
from .capabilities import CapabilityDetector, CapabilityKey
|
|
7
|
+
from .compatibility import CompatibilityState
|
|
8
|
+
from .contracts import AgentIntegrationContract
|
|
9
|
+
from .feature_flags import (
|
|
10
|
+
FeatureEffect,
|
|
11
|
+
FeatureFlagState,
|
|
12
|
+
permitted_effect,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AgentIntegrationMode(str, Enum):
|
|
17
|
+
TOKEN = "TOKEN"
|
|
18
|
+
PASSTHROUGH = "PASSTHROUGH"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class AgentIntegrationDecision:
|
|
23
|
+
agent_id: str
|
|
24
|
+
capability_key: CapabilityKey
|
|
25
|
+
endpoint: str
|
|
26
|
+
compatibility_state: CompatibilityState
|
|
27
|
+
effect: FeatureEffect
|
|
28
|
+
evidence_id: str
|
|
29
|
+
reason: str | None
|
|
30
|
+
mode: AgentIntegrationMode
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AgentIntegrationFramework:
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
detector: CapabilityDetector,
|
|
37
|
+
*,
|
|
38
|
+
flag_state: FeatureFlagState = FeatureFlagState.ENABLED,
|
|
39
|
+
) -> None:
|
|
40
|
+
self.detector = detector
|
|
41
|
+
self.flag_state = flag_state
|
|
42
|
+
|
|
43
|
+
def resolve(
|
|
44
|
+
self,
|
|
45
|
+
integration: AgentIntegrationContract,
|
|
46
|
+
) -> AgentIntegrationDecision:
|
|
47
|
+
detected = self.detector.detect(integration.capability_key)
|
|
48
|
+
if detected.profile is None:
|
|
49
|
+
compatibility_state = CompatibilityState.PASSTHROUGH_ONLY
|
|
50
|
+
evidence_id = "unknown"
|
|
51
|
+
reason = (
|
|
52
|
+
"unknown_capability"
|
|
53
|
+
if detected.compatibility.reason == "unknown_capability"
|
|
54
|
+
else "missing_capability_profile"
|
|
55
|
+
)
|
|
56
|
+
elif detected.compatibility.reason == "unknown_capability":
|
|
57
|
+
compatibility_state = CompatibilityState.PASSTHROUGH_ONLY
|
|
58
|
+
evidence_id = "unknown"
|
|
59
|
+
reason = "unknown_capability"
|
|
60
|
+
elif (
|
|
61
|
+
detected.profile.evidence_id == "unknown"
|
|
62
|
+
or detected.compatibility.evidence_id == "unknown"
|
|
63
|
+
or detected.profile.evidence_id != detected.compatibility.evidence_id
|
|
64
|
+
):
|
|
65
|
+
compatibility_state = CompatibilityState.PASSTHROUGH_ONLY
|
|
66
|
+
evidence_id = "unknown"
|
|
67
|
+
reason = "capability_evidence_mismatch"
|
|
68
|
+
else:
|
|
69
|
+
compatibility_state = detected.compatibility.state
|
|
70
|
+
evidence_id = detected.compatibility.evidence_id
|
|
71
|
+
reason = detected.compatibility.reason
|
|
72
|
+
effect = permitted_effect(self.flag_state, compatibility_state)
|
|
73
|
+
mode = (
|
|
74
|
+
AgentIntegrationMode.TOKEN
|
|
75
|
+
if effect is FeatureEffect.EXECUTE
|
|
76
|
+
else AgentIntegrationMode.PASSTHROUGH
|
|
77
|
+
)
|
|
78
|
+
return AgentIntegrationDecision(
|
|
79
|
+
agent_id=integration.agent_id,
|
|
80
|
+
capability_key=integration.capability_key,
|
|
81
|
+
endpoint=integration.endpoint,
|
|
82
|
+
compatibility_state=compatibility_state,
|
|
83
|
+
effect=effect,
|
|
84
|
+
evidence_id=evidence_id,
|
|
85
|
+
reason=reason,
|
|
86
|
+
mode=mode,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(frozen=True, slots=True)
|
|
91
|
+
class _AgentIntegrationDescriptor:
|
|
92
|
+
agent_id: str
|
|
93
|
+
capability_key: CapabilityKey
|
|
94
|
+
endpoint: str
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def codex_01540_reference() -> AgentIntegrationContract:
|
|
98
|
+
from .codex_recertification import CODEX_01540_RESPONSES_KEY
|
|
99
|
+
|
|
100
|
+
return _AgentIntegrationDescriptor(
|
|
101
|
+
agent_id="codex",
|
|
102
|
+
capability_key=CODEX_01540_RESPONSES_KEY,
|
|
103
|
+
endpoint="/v1/responses",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def build_codex_01540_agent_framework(
|
|
108
|
+
*,
|
|
109
|
+
flag_state: FeatureFlagState = FeatureFlagState.ENABLED,
|
|
110
|
+
) -> AgentIntegrationFramework:
|
|
111
|
+
from .codex_recertification import build_codex_01540_recertification
|
|
112
|
+
|
|
113
|
+
bundle = build_codex_01540_recertification()
|
|
114
|
+
return AgentIntegrationFramework(bundle.detector, flag_state=flag_state)
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any, Mapping, Sequence
|
|
6
|
+
|
|
7
|
+
from .model import ContextBlock, RequestEnvelope
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _set_path(root: Any, path: Sequence[Any], value: str) -> None:
|
|
11
|
+
target = root
|
|
12
|
+
for key in path[:-1]:
|
|
13
|
+
target = target[key]
|
|
14
|
+
target[path[-1]] = value
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _block(
|
|
18
|
+
block_id: str,
|
|
19
|
+
kind: str,
|
|
20
|
+
text: str,
|
|
21
|
+
*,
|
|
22
|
+
role: str | None = None,
|
|
23
|
+
turn: int = 0,
|
|
24
|
+
path: Sequence[Any] | None = None,
|
|
25
|
+
) -> ContextBlock:
|
|
26
|
+
metadata = {} if path is None else {"path": tuple(path)}
|
|
27
|
+
return ContextBlock(
|
|
28
|
+
block_id,
|
|
29
|
+
kind,
|
|
30
|
+
text,
|
|
31
|
+
role=role,
|
|
32
|
+
turn_index=turn,
|
|
33
|
+
metadata=metadata,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _encoded(value: Mapping[str, Any]) -> str:
|
|
38
|
+
return json.dumps(dict(value), sort_keys=True, separators=(",", ":"))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AnthropicMessagesAdapter:
|
|
42
|
+
protocol_id = "anthropic_messages"
|
|
43
|
+
|
|
44
|
+
def serialize(self, envelope: RequestEnvelope) -> dict[str, Any]:
|
|
45
|
+
payload = deepcopy(envelope.opaque["original_payload"])
|
|
46
|
+
for block in envelope.blocks:
|
|
47
|
+
path = block.metadata.get("path")
|
|
48
|
+
if path:
|
|
49
|
+
_set_path(payload, path, block.text)
|
|
50
|
+
return payload
|
|
51
|
+
|
|
52
|
+
def parse(self, payload: Mapping[str, Any]) -> RequestEnvelope:
|
|
53
|
+
original = deepcopy(dict(payload))
|
|
54
|
+
blocks: list[ContextBlock] = []
|
|
55
|
+
safe = True
|
|
56
|
+
|
|
57
|
+
system_blocks, system_safe = self._parse_system(payload.get("system"))
|
|
58
|
+
blocks.extend(system_blocks)
|
|
59
|
+
safe = safe and system_safe
|
|
60
|
+
|
|
61
|
+
tool_blocks, tools_safe = self._parse_tools(payload.get("tools"))
|
|
62
|
+
blocks.extend(tool_blocks)
|
|
63
|
+
safe = safe and tools_safe
|
|
64
|
+
|
|
65
|
+
messages = payload.get("messages")
|
|
66
|
+
if not isinstance(messages, list):
|
|
67
|
+
safe = False
|
|
68
|
+
messages = []
|
|
69
|
+
|
|
70
|
+
for index, message in enumerate(messages):
|
|
71
|
+
message_blocks, message_safe = self._parse_message(index, message)
|
|
72
|
+
blocks.extend(message_blocks)
|
|
73
|
+
safe = safe and message_safe
|
|
74
|
+
|
|
75
|
+
return RequestEnvelope(
|
|
76
|
+
blocks=tuple(blocks),
|
|
77
|
+
opaque={"original_payload": original},
|
|
78
|
+
adapter=self.protocol_id,
|
|
79
|
+
wire_safe=safe,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _parse_system(system: Any) -> tuple[list[ContextBlock], bool]:
|
|
84
|
+
if system is None:
|
|
85
|
+
return [], True
|
|
86
|
+
if isinstance(system, str):
|
|
87
|
+
return [
|
|
88
|
+
_block("system", "system", system, role="system", path=("system",))
|
|
89
|
+
], True
|
|
90
|
+
if not isinstance(system, list):
|
|
91
|
+
return [], False
|
|
92
|
+
|
|
93
|
+
blocks: list[ContextBlock] = []
|
|
94
|
+
safe = True
|
|
95
|
+
for index, part in enumerate(system):
|
|
96
|
+
if (
|
|
97
|
+
not isinstance(part, Mapping)
|
|
98
|
+
or part.get("type") != "text"
|
|
99
|
+
or not isinstance(part.get("text"), str)
|
|
100
|
+
):
|
|
101
|
+
safe = False
|
|
102
|
+
continue
|
|
103
|
+
cited = part.get("citations") is not None
|
|
104
|
+
blocks.append(
|
|
105
|
+
_block(
|
|
106
|
+
f"system-{index}",
|
|
107
|
+
"system",
|
|
108
|
+
part["text"],
|
|
109
|
+
role="system",
|
|
110
|
+
path=None if cited else ("system", index, "text"),
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
safe = safe and not cited
|
|
114
|
+
return blocks, safe
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _parse_tools(tools: Any) -> tuple[list[ContextBlock], bool]:
|
|
118
|
+
if tools is None:
|
|
119
|
+
return [], True
|
|
120
|
+
if not isinstance(tools, list):
|
|
121
|
+
return [], False
|
|
122
|
+
blocks: list[ContextBlock] = []
|
|
123
|
+
safe = True
|
|
124
|
+
for index, tool in enumerate(tools):
|
|
125
|
+
if not isinstance(tool, Mapping):
|
|
126
|
+
safe = False
|
|
127
|
+
continue
|
|
128
|
+
blocks.append(
|
|
129
|
+
_block(
|
|
130
|
+
f"tool-schema-{index}",
|
|
131
|
+
"tool_schema",
|
|
132
|
+
_encoded(tool),
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
return blocks, safe
|
|
136
|
+
|
|
137
|
+
def _parse_message(
|
|
138
|
+
self,
|
|
139
|
+
index: int,
|
|
140
|
+
message: Any,
|
|
141
|
+
) -> tuple[list[ContextBlock], bool]:
|
|
142
|
+
if not isinstance(message, Mapping):
|
|
143
|
+
return [], False
|
|
144
|
+
role = message.get("role")
|
|
145
|
+
if role not in {"user", "assistant"}:
|
|
146
|
+
return [], False
|
|
147
|
+
|
|
148
|
+
content = message.get("content")
|
|
149
|
+
if isinstance(content, str):
|
|
150
|
+
return [
|
|
151
|
+
_block(
|
|
152
|
+
f"message-{index}",
|
|
153
|
+
role,
|
|
154
|
+
content,
|
|
155
|
+
role=role,
|
|
156
|
+
turn=index,
|
|
157
|
+
path=("messages", index, "content"),
|
|
158
|
+
)
|
|
159
|
+
], True
|
|
160
|
+
|
|
161
|
+
if not isinstance(content, list):
|
|
162
|
+
return [], False
|
|
163
|
+
|
|
164
|
+
blocks: list[ContextBlock] = []
|
|
165
|
+
safe = True
|
|
166
|
+
for part_index, part in enumerate(content):
|
|
167
|
+
part_blocks, part_safe = self._parse_content_block(
|
|
168
|
+
message_index=index,
|
|
169
|
+
part_index=part_index,
|
|
170
|
+
role=role,
|
|
171
|
+
part=part,
|
|
172
|
+
)
|
|
173
|
+
blocks.extend(part_blocks)
|
|
174
|
+
safe = safe and part_safe
|
|
175
|
+
return blocks, safe
|
|
176
|
+
|
|
177
|
+
def _parse_content_block(
|
|
178
|
+
self,
|
|
179
|
+
*,
|
|
180
|
+
message_index: int,
|
|
181
|
+
part_index: int,
|
|
182
|
+
role: str,
|
|
183
|
+
part: Any,
|
|
184
|
+
) -> tuple[list[ContextBlock], bool]:
|
|
185
|
+
if not isinstance(part, Mapping):
|
|
186
|
+
return [], False
|
|
187
|
+
|
|
188
|
+
part_type = part.get("type")
|
|
189
|
+
if part_type == "text":
|
|
190
|
+
text = part.get("text")
|
|
191
|
+
if not isinstance(text, str):
|
|
192
|
+
return [], False
|
|
193
|
+
cited = part.get("citations") is not None
|
|
194
|
+
return [
|
|
195
|
+
_block(
|
|
196
|
+
f"message-{message_index}-content-{part_index}",
|
|
197
|
+
role,
|
|
198
|
+
text,
|
|
199
|
+
role=role,
|
|
200
|
+
turn=message_index,
|
|
201
|
+
path=(
|
|
202
|
+
None
|
|
203
|
+
if cited
|
|
204
|
+
else ("messages", message_index, "content", part_index, "text")
|
|
205
|
+
),
|
|
206
|
+
)
|
|
207
|
+
], not cited
|
|
208
|
+
|
|
209
|
+
if part_type == "tool_use":
|
|
210
|
+
valid_tool_use = (
|
|
211
|
+
role == "assistant"
|
|
212
|
+
and isinstance(part.get("id"), str)
|
|
213
|
+
and isinstance(part.get("name"), str)
|
|
214
|
+
and isinstance(part.get("input"), Mapping)
|
|
215
|
+
)
|
|
216
|
+
return [
|
|
217
|
+
_block(
|
|
218
|
+
f"message-{message_index}-tool-use-{part_index}",
|
|
219
|
+
"tool_call",
|
|
220
|
+
_encoded(part),
|
|
221
|
+
role=role,
|
|
222
|
+
turn=message_index,
|
|
223
|
+
)
|
|
224
|
+
], valid_tool_use
|
|
225
|
+
|
|
226
|
+
if part_type == "tool_result":
|
|
227
|
+
return self._parse_tool_result(
|
|
228
|
+
message_index=message_index,
|
|
229
|
+
part_index=part_index,
|
|
230
|
+
role=role,
|
|
231
|
+
part=part,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if part_type in {"thinking", "redacted_thinking"}:
|
|
235
|
+
return [
|
|
236
|
+
_block(
|
|
237
|
+
f"message-{message_index}-protocol-{part_index}",
|
|
238
|
+
"protocol_state",
|
|
239
|
+
_encoded(part),
|
|
240
|
+
role=role,
|
|
241
|
+
turn=message_index,
|
|
242
|
+
)
|
|
243
|
+
], False
|
|
244
|
+
|
|
245
|
+
return [], False
|
|
246
|
+
|
|
247
|
+
@staticmethod
|
|
248
|
+
def _parse_tool_result(
|
|
249
|
+
*,
|
|
250
|
+
message_index: int,
|
|
251
|
+
part_index: int,
|
|
252
|
+
role: str,
|
|
253
|
+
part: Mapping[str, Any],
|
|
254
|
+
) -> tuple[list[ContextBlock], bool]:
|
|
255
|
+
content = part.get("content")
|
|
256
|
+
base_path = ("messages", message_index, "content", part_index, "content")
|
|
257
|
+
valid_reference = role == "user" and isinstance(part.get("tool_use_id"), str)
|
|
258
|
+
if isinstance(content, str):
|
|
259
|
+
return [
|
|
260
|
+
_block(
|
|
261
|
+
f"message-{message_index}-tool-result-{part_index}",
|
|
262
|
+
"tool_output",
|
|
263
|
+
content,
|
|
264
|
+
role="tool",
|
|
265
|
+
turn=message_index,
|
|
266
|
+
path=base_path if valid_reference else None,
|
|
267
|
+
)
|
|
268
|
+
], valid_reference
|
|
269
|
+
if not isinstance(content, list):
|
|
270
|
+
return [], False
|
|
271
|
+
|
|
272
|
+
blocks: list[ContextBlock] = []
|
|
273
|
+
safe = valid_reference
|
|
274
|
+
for nested_index, nested in enumerate(content):
|
|
275
|
+
if (
|
|
276
|
+
not isinstance(nested, Mapping)
|
|
277
|
+
or nested.get("type") != "text"
|
|
278
|
+
or not isinstance(nested.get("text"), str)
|
|
279
|
+
):
|
|
280
|
+
safe = False
|
|
281
|
+
continue
|
|
282
|
+
cited = nested.get("citations") is not None
|
|
283
|
+
blocks.append(
|
|
284
|
+
_block(
|
|
285
|
+
(
|
|
286
|
+
f"message-{message_index}-tool-result-"
|
|
287
|
+
f"{part_index}-content-{nested_index}"
|
|
288
|
+
),
|
|
289
|
+
"tool_output",
|
|
290
|
+
nested["text"],
|
|
291
|
+
role="tool",
|
|
292
|
+
turn=message_index,
|
|
293
|
+
path=(
|
|
294
|
+
None
|
|
295
|
+
if cited or not valid_reference
|
|
296
|
+
else base_path + (nested_index, "text")
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
safe = safe and not cited
|
|
301
|
+
return blocks, safe
|