copilotkit-intelligence-runtime 0.1.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.
- copilotkit_intelligence/__init__.py +62 -0
- copilotkit_intelligence/client.py +805 -0
- copilotkit_intelligence/entitlements.py +142 -0
- copilotkit_intelligence/inspector.py +182 -0
- copilotkit_intelligence/learned_skills.py +98 -0
- copilotkit_intelligence/py.typed +0 -0
- copilotkit_intelligence/resources.py +134 -0
- copilotkit_intelligence_runtime-0.1.0.dist-info/METADATA +403 -0
- copilotkit_intelligence_runtime-0.1.0.dist-info/RECORD +22 -0
- copilotkit_intelligence_runtime-0.1.0.dist-info/WHEEL +4 -0
- copilotkit_intelligence_runtime-0.1.0.dist-info/licenses/LICENSE +21 -0
- copilotkit_runtime/__init__.py +27 -0
- copilotkit_runtime/a2ui.py +559 -0
- copilotkit_runtime/agents.py +64 -0
- copilotkit_runtime/finalizer.py +75 -0
- copilotkit_runtime/gateway.py +287 -0
- copilotkit_runtime/mcp_apps.py +299 -0
- copilotkit_runtime/models.py +77 -0
- copilotkit_runtime/platform.py +67 -0
- copilotkit_runtime/py.typed +0 -0
- copilotkit_runtime/runtime.py +878 -0
- copilotkit_runtime/telemetry.py +263 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Typed Runtime entitlement responses and strict platform normalization."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from typing import Any, Literal, NotRequired, TypedDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RuntimeEntitlement(TypedDict):
|
|
8
|
+
"""The Runtime grant and its platform-defined features and limits."""
|
|
9
|
+
|
|
10
|
+
active: bool
|
|
11
|
+
source: Literal[
|
|
12
|
+
"managedOrgSubscription", "selfHostedDeploymentLicense", "awsMarketplaceDeploymentLicense"
|
|
13
|
+
]
|
|
14
|
+
features: dict[str, bool]
|
|
15
|
+
limits: dict[str, float]
|
|
16
|
+
planCode: NotRequired[str]
|
|
17
|
+
entitlementSource: NotRequired[str]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RuntimeEntitlementProblem(TypedDict):
|
|
21
|
+
"""A structured reason why the platform cannot supply a ready grant."""
|
|
22
|
+
|
|
23
|
+
code: str
|
|
24
|
+
message: str
|
|
25
|
+
retryable: bool
|
|
26
|
+
requestId: NotRequired[str]
|
|
27
|
+
traceId: NotRequired[str]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RuntimeEntitlementReady(TypedDict):
|
|
31
|
+
"""A resolved grant. An inactive grant does not authorize Runtime access."""
|
|
32
|
+
|
|
33
|
+
status: Literal["ready"]
|
|
34
|
+
entitlement: RuntimeEntitlement
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RuntimeEntitlementUnavailable(TypedDict):
|
|
38
|
+
"""A non-ready platform result with retry and correlation details."""
|
|
39
|
+
|
|
40
|
+
status: Literal["degraded", "misconfigured", "unavailable"]
|
|
41
|
+
error: RuntimeEntitlementProblem
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
RuntimeEntitlementResponse = RuntimeEntitlementReady | RuntimeEntitlementUnavailable
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _finite_number(value: Any) -> bool:
|
|
48
|
+
"""Reject booleans and numbers outside the finite JavaScript number range."""
|
|
49
|
+
if type(value) not in (int, float):
|
|
50
|
+
return False
|
|
51
|
+
try:
|
|
52
|
+
return math.isfinite(value)
|
|
53
|
+
except OverflowError:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _entitlement(value: Any) -> RuntimeEntitlement | None:
|
|
58
|
+
"""Reject malformed or unknown authority fields instead of widening a grant."""
|
|
59
|
+
required = {"active", "source", "features", "limits"}
|
|
60
|
+
if (
|
|
61
|
+
not isinstance(value, dict)
|
|
62
|
+
or not required <= value.keys()
|
|
63
|
+
or value.keys() - required - {"planCode", "entitlementSource"}
|
|
64
|
+
):
|
|
65
|
+
return None
|
|
66
|
+
if type(value["active"]) is not bool or value["source"] not in (
|
|
67
|
+
"managedOrgSubscription",
|
|
68
|
+
"selfHostedDeploymentLicense",
|
|
69
|
+
"awsMarketplaceDeploymentLicense",
|
|
70
|
+
):
|
|
71
|
+
return None
|
|
72
|
+
if not isinstance(value["features"], dict) or any(
|
|
73
|
+
not isinstance(key, str) or type(flag) is not bool
|
|
74
|
+
for key, flag in value["features"].items()
|
|
75
|
+
):
|
|
76
|
+
return None
|
|
77
|
+
if not isinstance(value["limits"], dict) or any(
|
|
78
|
+
not isinstance(key, str) or not _finite_number(limit)
|
|
79
|
+
for key, limit in value["limits"].items()
|
|
80
|
+
):
|
|
81
|
+
return None
|
|
82
|
+
result: RuntimeEntitlement = {
|
|
83
|
+
"active": value["active"],
|
|
84
|
+
"source": value["source"],
|
|
85
|
+
"features": dict(value["features"]),
|
|
86
|
+
"limits": dict(value["limits"]),
|
|
87
|
+
}
|
|
88
|
+
if "planCode" in value:
|
|
89
|
+
if not isinstance(value["planCode"], str):
|
|
90
|
+
return None
|
|
91
|
+
result["planCode"] = value["planCode"]
|
|
92
|
+
if "entitlementSource" in value:
|
|
93
|
+
if not isinstance(value["entitlementSource"], str):
|
|
94
|
+
return None
|
|
95
|
+
result["entitlementSource"] = value["entitlementSource"]
|
|
96
|
+
return result
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def normalize_runtime_entitlements(value: Any) -> RuntimeEntitlementResponse | None:
|
|
100
|
+
"""Accept the current strict union or the legacy flat organization response."""
|
|
101
|
+
if not isinstance(value, dict):
|
|
102
|
+
return None
|
|
103
|
+
if value.get("status") == "ready" and value.keys() == {"status", "entitlement"}:
|
|
104
|
+
grant = _entitlement(value["entitlement"])
|
|
105
|
+
return {"status": "ready", "entitlement": grant} if grant is not None else None
|
|
106
|
+
if value.get("status") in ("degraded", "misconfigured", "unavailable") and value.keys() == {
|
|
107
|
+
"status",
|
|
108
|
+
"error",
|
|
109
|
+
}:
|
|
110
|
+
error = value["error"]
|
|
111
|
+
required = {"code", "message", "retryable"}
|
|
112
|
+
if (
|
|
113
|
+
not isinstance(error, dict)
|
|
114
|
+
or not required <= error.keys()
|
|
115
|
+
or error.keys() - required - {"requestId", "traceId"}
|
|
116
|
+
):
|
|
117
|
+
return None
|
|
118
|
+
if (
|
|
119
|
+
not isinstance(error["code"], str)
|
|
120
|
+
or not isinstance(error["message"], str)
|
|
121
|
+
or type(error["retryable"]) is not bool
|
|
122
|
+
):
|
|
123
|
+
return None
|
|
124
|
+
problem: RuntimeEntitlementProblem = {
|
|
125
|
+
"code": error["code"],
|
|
126
|
+
"message": error["message"],
|
|
127
|
+
"retryable": error["retryable"],
|
|
128
|
+
}
|
|
129
|
+
if "requestId" in error:
|
|
130
|
+
if not isinstance(error["requestId"], str):
|
|
131
|
+
return None
|
|
132
|
+
problem["requestId"] = error["requestId"]
|
|
133
|
+
if "traceId" in error:
|
|
134
|
+
if not isinstance(error["traceId"], str):
|
|
135
|
+
return None
|
|
136
|
+
problem["traceId"] = error["traceId"]
|
|
137
|
+
return {"status": value["status"], "error": problem}
|
|
138
|
+
if isinstance(value.get("organizationId"), str):
|
|
139
|
+
grant = _entitlement({key: item for key, item in value.items() if key != "organizationId"})
|
|
140
|
+
if grant is not None:
|
|
141
|
+
return {"status": "ready", "entitlement": grant}
|
|
142
|
+
return None
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Typed, sanitized Inspector metadata from the Intelligence project."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, NotRequired, TypedDict
|
|
4
|
+
from urllib.parse import unquote, urlsplit
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InspectorIdentity(TypedDict):
|
|
10
|
+
"""Project and organization display names."""
|
|
11
|
+
|
|
12
|
+
organizationName: str
|
|
13
|
+
projectName: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InspectorPlan(TypedDict):
|
|
17
|
+
"""The plan identifier and display label."""
|
|
18
|
+
|
|
19
|
+
code: str
|
|
20
|
+
label: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InspectorLicense(TypedDict):
|
|
24
|
+
"""The displayed license state, not an authorization grant."""
|
|
25
|
+
|
|
26
|
+
state: Literal["valid", "none", "expired", "unknown"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class InspectorAction(TypedDict):
|
|
30
|
+
"""A plan action with a URL that excludes credentials, queries, and fragments."""
|
|
31
|
+
|
|
32
|
+
kind: Literal["manage_plan", "renew", "enable_intelligence"]
|
|
33
|
+
url: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class InspectorFiniteLimit(TypedDict):
|
|
37
|
+
"""A positive, finite usage limit."""
|
|
38
|
+
|
|
39
|
+
kind: Literal["finite"]
|
|
40
|
+
value: int
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class InspectorUnlimitedLimit(TypedDict):
|
|
44
|
+
"""A plan with no finite usage limit."""
|
|
45
|
+
|
|
46
|
+
kind: Literal["unlimited"]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InspectorUnknownLimit(TypedDict):
|
|
50
|
+
"""A plan whose usage limit is not available."""
|
|
51
|
+
|
|
52
|
+
kind: Literal["unknown"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
InspectorUsageLimit = InspectorFiniteLimit | InspectorUnlimitedLimit | InspectorUnknownLimit
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class InspectorUsage(TypedDict):
|
|
59
|
+
"""Usage counts and the plan limit."""
|
|
60
|
+
|
|
61
|
+
used: int
|
|
62
|
+
limit: InspectorUsageLimit
|
|
63
|
+
expiringSoonCount: NotRequired[int]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class InspectorMetadata(TypedDict):
|
|
67
|
+
"""Version 1 metadata with independent optional display modules."""
|
|
68
|
+
|
|
69
|
+
schemaVersion: Literal[1]
|
|
70
|
+
identity: NotRequired[InspectorIdentity]
|
|
71
|
+
plan: NotRequired[InspectorPlan]
|
|
72
|
+
license: NotRequired[InspectorLicense]
|
|
73
|
+
action: NotRequired[InspectorAction]
|
|
74
|
+
usage: NotRequired[InspectorUsage]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _text(value: object) -> str | None:
|
|
78
|
+
"""Trim the ECMAScript whitespace set used by the TypeScript parser."""
|
|
79
|
+
if not isinstance(value, str):
|
|
80
|
+
return None
|
|
81
|
+
return (
|
|
82
|
+
value.strip(
|
|
83
|
+
"\u0009\u000a\u000b\u000c\u000d\u0020\u00a0\u1680"
|
|
84
|
+
"\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007"
|
|
85
|
+
"\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff"
|
|
86
|
+
)
|
|
87
|
+
or None
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _integer(value: object, minimum: int = 0) -> int | None:
|
|
92
|
+
"""Accept only JSON numbers within JavaScript's safe integer range."""
|
|
93
|
+
if type(value) not in (int, float):
|
|
94
|
+
return None
|
|
95
|
+
if (
|
|
96
|
+
isinstance(value, (int, float))
|
|
97
|
+
and minimum <= value <= 9007199254740991
|
|
98
|
+
and value == int(value)
|
|
99
|
+
):
|
|
100
|
+
return int(value)
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _action_url(value: object) -> str | None:
|
|
105
|
+
"""Permit HTTPS and loopback HTTP links without embedded private parameters."""
|
|
106
|
+
url = _text(value)
|
|
107
|
+
if url is None or "?" in url or "#" in url or "://" not in url:
|
|
108
|
+
return None
|
|
109
|
+
authority = url.split("://", 1)[1].split("/", 1)[0]
|
|
110
|
+
if "@" in authority:
|
|
111
|
+
return None
|
|
112
|
+
try:
|
|
113
|
+
# Browsers treat backslashes as path separators in HTTP(S) URLs.
|
|
114
|
+
candidate = url.replace("\\", "/")
|
|
115
|
+
structural = urlsplit(candidate)
|
|
116
|
+
structural.port # Reject malformed and out-of-range ports.
|
|
117
|
+
parsed = httpx.URL(candidate)
|
|
118
|
+
host = unquote(parsed.host, errors="strict")
|
|
119
|
+
except (httpx.InvalidURL, ValueError, UnicodeError):
|
|
120
|
+
return None
|
|
121
|
+
if (
|
|
122
|
+
not host
|
|
123
|
+
or parsed.userinfo
|
|
124
|
+
or any(character in host for character in "\x00\t\n\r #%/<>?@[\\]^|")
|
|
125
|
+
):
|
|
126
|
+
return None
|
|
127
|
+
if parsed.scheme == "https" or (
|
|
128
|
+
parsed.scheme == "http" and parsed.host in ("localhost", "127.0.0.1", "::1")
|
|
129
|
+
):
|
|
130
|
+
return url
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def parse_inspector_metadata(value: object) -> InspectorMetadata | None:
|
|
135
|
+
"""Copy supported fields without letting one invalid module hide valid modules."""
|
|
136
|
+
if (
|
|
137
|
+
not isinstance(value, dict)
|
|
138
|
+
or type(value.get("schemaVersion")) not in (int, float)
|
|
139
|
+
or value.get("schemaVersion") != 1
|
|
140
|
+
):
|
|
141
|
+
return None
|
|
142
|
+
result: InspectorMetadata = {"schemaVersion": 1}
|
|
143
|
+
identity = value.get("identity")
|
|
144
|
+
if isinstance(identity, dict):
|
|
145
|
+
organization = _text(identity.get("organizationName"))
|
|
146
|
+
project = _text(identity.get("projectName"))
|
|
147
|
+
if organization is not None and project is not None:
|
|
148
|
+
result["identity"] = {"organizationName": organization, "projectName": project}
|
|
149
|
+
plan = value.get("plan")
|
|
150
|
+
if isinstance(plan, dict):
|
|
151
|
+
code, label = _text(plan.get("code")), _text(plan.get("label"))
|
|
152
|
+
if code is not None and label is not None:
|
|
153
|
+
result["plan"] = {"code": code, "label": label}
|
|
154
|
+
license = value.get("license")
|
|
155
|
+
if isinstance(license, dict):
|
|
156
|
+
state = license.get("state")
|
|
157
|
+
if state in ("valid", "none", "expired", "unknown"):
|
|
158
|
+
result["license"] = {"state": state}
|
|
159
|
+
action = value.get("action")
|
|
160
|
+
if isinstance(action, dict):
|
|
161
|
+
kind, url = action.get("kind"), _action_url(action.get("url"))
|
|
162
|
+
if kind in ("manage_plan", "renew", "enable_intelligence") and url is not None:
|
|
163
|
+
result["action"] = {"kind": kind, "url": url}
|
|
164
|
+
usage = value.get("usage")
|
|
165
|
+
if isinstance(usage, dict):
|
|
166
|
+
used, limit = _integer(usage.get("used")), usage.get("limit")
|
|
167
|
+
if used is not None and isinstance(limit, dict):
|
|
168
|
+
parsed_limit: InspectorUsageLimit | None = None
|
|
169
|
+
if limit.get("kind") == "finite":
|
|
170
|
+
count = _integer(limit.get("value"), 1)
|
|
171
|
+
if count is not None:
|
|
172
|
+
parsed_limit = {"kind": "finite", "value": count}
|
|
173
|
+
elif limit.get("kind") == "unlimited":
|
|
174
|
+
parsed_limit = {"kind": "unlimited"}
|
|
175
|
+
elif limit.get("kind") == "unknown":
|
|
176
|
+
parsed_limit = {"kind": "unknown"}
|
|
177
|
+
if parsed_limit is not None:
|
|
178
|
+
result["usage"] = {"used": used, "limit": parsed_limit}
|
|
179
|
+
expiring = _integer(usage.get("expiringSoonCount"))
|
|
180
|
+
if expiring is not None:
|
|
181
|
+
result["usage"]["expiringSoonCount"] = expiring
|
|
182
|
+
return result
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Safe learned-skill delivery errors and raw snapshot result shapes."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, TypeAlias, TypedDict, cast
|
|
4
|
+
|
|
5
|
+
LearnedSkillsErrorCode: TypeAlias = Literal[
|
|
6
|
+
"INVALID_CONFIG",
|
|
7
|
+
"AUTHENTICATION_FAILED",
|
|
8
|
+
"AUTHORIZATION_FAILED",
|
|
9
|
+
"ENTITLEMENT_REQUIRED",
|
|
10
|
+
"DELIVERY_DISABLED",
|
|
11
|
+
"CONTAINER_NOT_FOUND",
|
|
12
|
+
"REVISION_NOT_FOUND",
|
|
13
|
+
"REVISION_REVOKED",
|
|
14
|
+
"NETWORK_ERROR",
|
|
15
|
+
"TIMEOUT",
|
|
16
|
+
"INVALID_SNAPSHOT",
|
|
17
|
+
"UNSUPPORTED_SERVER",
|
|
18
|
+
]
|
|
19
|
+
_MESSAGES: dict[LearnedSkillsErrorCode, str] = {
|
|
20
|
+
"INVALID_CONFIG": "Invalid learned-skills request configuration.",
|
|
21
|
+
"AUTHENTICATION_FAILED": "Learned-skills authentication failed.",
|
|
22
|
+
"AUTHORIZATION_FAILED": "Learned-skills access was denied.",
|
|
23
|
+
"ENTITLEMENT_REQUIRED": "Learned-skills delivery requires an entitlement.",
|
|
24
|
+
"DELIVERY_DISABLED": "Learned-skills delivery is disabled.",
|
|
25
|
+
"CONTAINER_NOT_FOUND": "The learning container was not found.",
|
|
26
|
+
"REVISION_NOT_FOUND": "The learned-skills revision was not found.",
|
|
27
|
+
"REVISION_REVOKED": "The learned-skills revision was revoked.",
|
|
28
|
+
"NETWORK_ERROR": "The learned-skills request failed during transport.",
|
|
29
|
+
"TIMEOUT": "The learned-skills request timed out.",
|
|
30
|
+
"INVALID_SNAPSHOT": "The learned-skills response metadata is invalid.",
|
|
31
|
+
"UNSUPPORTED_SERVER": "The server returned an unsupported learned-skills response.",
|
|
32
|
+
}
|
|
33
|
+
_DENIAL_CODES = frozenset(
|
|
34
|
+
{
|
|
35
|
+
"AUTHENTICATION_FAILED",
|
|
36
|
+
"AUTHORIZATION_FAILED",
|
|
37
|
+
"ENTITLEMENT_REQUIRED",
|
|
38
|
+
"DELIVERY_DISABLED",
|
|
39
|
+
"CONTAINER_NOT_FOUND",
|
|
40
|
+
"REVISION_NOT_FOUND",
|
|
41
|
+
"REVISION_REVOKED",
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class LearnedSkillsError(Exception):
|
|
47
|
+
"""Stable safe failure; an optional cause is available for explicit diagnostics."""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self, code: LearnedSkillsErrorCode, retryable: bool, cause: BaseException | None = None
|
|
51
|
+
) -> None:
|
|
52
|
+
self.code = code
|
|
53
|
+
self.message = _MESSAGES[code]
|
|
54
|
+
self.retryable = retryable
|
|
55
|
+
self.cause = cause
|
|
56
|
+
super().__init__(self.message)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class LearnedSkillsSnapshot(TypedDict):
|
|
60
|
+
"""Raw ZIP response; archive validation belongs to the framework adapter."""
|
|
61
|
+
|
|
62
|
+
status: Literal["snapshot"]
|
|
63
|
+
bytes: bytes
|
|
64
|
+
revision: str
|
|
65
|
+
etag: str
|
|
66
|
+
contentType: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class LearnedSkillsUnchanged(TypedDict):
|
|
70
|
+
"""Conditional response; the caller owns the previous snapshot."""
|
|
71
|
+
|
|
72
|
+
status: Literal["unchanged"]
|
|
73
|
+
revision: str
|
|
74
|
+
etag: str
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
LearnedSkillsSnapshotResult: TypeAlias = LearnedSkillsSnapshot | LearnedSkillsUnchanged
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def response_error(status: int, body: object) -> LearnedSkillsError:
|
|
81
|
+
"""Discard response text and preserve only recognized structured failure fields."""
|
|
82
|
+
if status == 401:
|
|
83
|
+
return LearnedSkillsError("AUTHENTICATION_FAILED", False)
|
|
84
|
+
error = body.get("error") if isinstance(body, dict) else None
|
|
85
|
+
if (
|
|
86
|
+
isinstance(error, dict)
|
|
87
|
+
and isinstance(error.get("code"), str)
|
|
88
|
+
and error["code"] in _MESSAGES
|
|
89
|
+
and isinstance(error.get("message"), str)
|
|
90
|
+
and isinstance(error.get("category"), str)
|
|
91
|
+
and isinstance(error.get("retryable"), bool)
|
|
92
|
+
):
|
|
93
|
+
code = cast(LearnedSkillsErrorCode, error["code"])
|
|
94
|
+
if status != 403 or code in _DENIAL_CODES:
|
|
95
|
+
return LearnedSkillsError(code, error["retryable"])
|
|
96
|
+
return LearnedSkillsError(
|
|
97
|
+
"AUTHORIZATION_FAILED" if status == 403 else "UNSUPPORTED_SERVER", False
|
|
98
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Public resource shapes. Results remain dictionaries with their wire field names."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, NotRequired, TypeAlias, TypedDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ThreadSummary(TypedDict):
|
|
7
|
+
"""Thread metadata without message history."""
|
|
8
|
+
|
|
9
|
+
id: str
|
|
10
|
+
name: str | None
|
|
11
|
+
lastRunAt: NotRequired[str]
|
|
12
|
+
lastUpdatedAt: NotRequired[str]
|
|
13
|
+
createdAt: NotRequired[str]
|
|
14
|
+
updatedAt: NotRequired[str]
|
|
15
|
+
archived: NotRequired[bool]
|
|
16
|
+
agentId: NotRequired[str]
|
|
17
|
+
createdById: NotRequired[str]
|
|
18
|
+
organizationId: NotRequired[str]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ListThreadsResponse(TypedDict):
|
|
22
|
+
"""A page of threads with its cursor and realtime join credentials."""
|
|
23
|
+
|
|
24
|
+
threads: list[ThreadSummary]
|
|
25
|
+
joinCode: str
|
|
26
|
+
joinToken: NotRequired[str]
|
|
27
|
+
nextCursor: NotRequired[str | None]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ThreadResolution(TypedDict):
|
|
31
|
+
"""A thread and whether this call created it."""
|
|
32
|
+
|
|
33
|
+
thread: ThreadSummary
|
|
34
|
+
created: bool
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ThreadToolCall(TypedDict):
|
|
38
|
+
"""A persisted tool call with JSON-encoded arguments."""
|
|
39
|
+
|
|
40
|
+
id: str
|
|
41
|
+
name: str
|
|
42
|
+
args: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ThreadMessage(TypedDict):
|
|
46
|
+
"""A persisted AG-UI message with optional structured content."""
|
|
47
|
+
|
|
48
|
+
id: str
|
|
49
|
+
role: str
|
|
50
|
+
content: NotRequired[object]
|
|
51
|
+
activityType: NotRequired[str]
|
|
52
|
+
toolCalls: NotRequired[list[ThreadToolCall]]
|
|
53
|
+
toolCallId: NotRequired[str]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ThreadMessagesResponse(TypedDict):
|
|
57
|
+
"""Persisted messages in chronological order."""
|
|
58
|
+
|
|
59
|
+
messages: list[ThreadMessage]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ThreadInspectEvent(TypedDict):
|
|
63
|
+
"""A persisted event. Additional event fields remain in the dictionary."""
|
|
64
|
+
|
|
65
|
+
type: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ThreadEventsResponse(TypedDict):
|
|
69
|
+
"""Persisted events with decode failures and the event-cap marker."""
|
|
70
|
+
|
|
71
|
+
events: list[ThreadInspectEvent]
|
|
72
|
+
decodeErrorRowIds: list[str]
|
|
73
|
+
truncated: bool
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ThreadNoSnapshot(TypedDict):
|
|
77
|
+
"""The thread has no state snapshot."""
|
|
78
|
+
|
|
79
|
+
kind: Literal["no-snapshot"]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ThreadSnapshotDecodeError(TypedDict):
|
|
83
|
+
"""The platform could not decode the stored snapshot."""
|
|
84
|
+
|
|
85
|
+
kind: Literal["snapshot-decode-error"]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ThreadSnapshot(TypedDict):
|
|
89
|
+
"""Folded state and the number of deltas the platform skipped."""
|
|
90
|
+
|
|
91
|
+
kind: Literal["snapshot"]
|
|
92
|
+
state: object
|
|
93
|
+
skippedDeltas: int
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
ThreadStateResponse: TypeAlias = ThreadNoSnapshot | ThreadSnapshotDecodeError | ThreadSnapshot
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AnnotateResponse(TypedDict):
|
|
100
|
+
"""The annotation ID and whether the platform recognized a repeated write."""
|
|
101
|
+
|
|
102
|
+
id: str
|
|
103
|
+
duplicate: bool
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class MemorySummary(TypedDict):
|
|
107
|
+
"""A stored memory, with an optional relevance score from recall."""
|
|
108
|
+
|
|
109
|
+
id: str
|
|
110
|
+
kind: str
|
|
111
|
+
scope: str
|
|
112
|
+
content: str
|
|
113
|
+
sourceThreadIds: list[str]
|
|
114
|
+
invalidatedAt: str | None
|
|
115
|
+
score: NotRequired[float]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ListMemoriesResponse(TypedDict):
|
|
119
|
+
"""Memories visible to the application user under the supplied grant."""
|
|
120
|
+
|
|
121
|
+
memories: list[MemorySummary]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class RecallMemoriesResponse(TypedDict):
|
|
125
|
+
"""Memories that match a recall query, with their relevance scores."""
|
|
126
|
+
|
|
127
|
+
memories: list[MemorySummary]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class SaveMemoryResponse(MemorySummary):
|
|
131
|
+
"""The saved memory and optional merge or replacement markers."""
|
|
132
|
+
|
|
133
|
+
absorbed: NotRequired[bool]
|
|
134
|
+
retiredId: NotRequired[str]
|