reflex-agentkit 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.
- reflex_agentkit/__init__.py +68 -0
- reflex_agentkit/adapters/__init__.py +1 -0
- reflex_agentkit/adapters/_require.py +27 -0
- reflex_agentkit/adapters/langgraph.py +277 -0
- reflex_agentkit/adapters/pydantic_ai.py +93 -0
- reflex_agentkit/components/__init__.py +21 -0
- reflex_agentkit/components/approval.py +123 -0
- reflex_agentkit/components/chat.py +42 -0
- reflex_agentkit/components/composer.py +44 -0
- reflex_agentkit/components/messages.py +104 -0
- reflex_agentkit/components/shared_state.py +32 -0
- reflex_agentkit/components/status.py +41 -0
- reflex_agentkit/components/tools.py +97 -0
- reflex_agentkit/core/__init__.py +56 -0
- reflex_agentkit/core/_log.py +47 -0
- reflex_agentkit/core/_patch.py +46 -0
- reflex_agentkit/core/clients/__init__.py +14 -0
- reflex_agentkit/core/clients/base.py +71 -0
- reflex_agentkit/core/clients/http.py +147 -0
- reflex_agentkit/core/clients/in_process.py +67 -0
- reflex_agentkit/core/clients/sse.py +75 -0
- reflex_agentkit/core/commands.py +589 -0
- reflex_agentkit/core/conversation.py +93 -0
- reflex_agentkit/core/errors.py +74 -0
- reflex_agentkit/core/models.py +243 -0
- reflex_agentkit/core/reducer.py +1078 -0
- reflex_agentkit/core/run_input.py +122 -0
- reflex_agentkit/core/tools.py +204 -0
- reflex_agentkit/py.typed +0 -0
- reflex_agentkit/state/__init__.py +5 -0
- reflex_agentkit/state/_driver.py +520 -0
- reflex_agentkit/state/_projection.py +120 -0
- reflex_agentkit/state/_registry.py +46 -0
- reflex_agentkit/state/agent_state.py +496 -0
- reflex_agentkit-0.1.0.dist-info/METADATA +457 -0
- reflex_agentkit-0.1.0.dist-info/RECORD +38 -0
- reflex_agentkit-0.1.0.dist-info/WHEEL +4 -0
- reflex_agentkit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Exception hierarchy and error codes (API §9, DM §6).
|
|
2
|
+
|
|
3
|
+
Exceptions are raised to *developers*. The browser only ever sees an :class:`ErrorCode`
|
|
4
|
+
and a sanitized message in the state vars.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from typing import Any, Literal, TypeAlias
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AdapterNotInstalledError",
|
|
14
|
+
"AgentHTTPError",
|
|
15
|
+
"AgentKitError",
|
|
16
|
+
"AgentStreamError",
|
|
17
|
+
"ConfigurationError",
|
|
18
|
+
"ErrorCode",
|
|
19
|
+
"StreamErrorReason",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ErrorCode(StrEnum):
|
|
24
|
+
"""Codes surfaced in the ``error_code`` state var (API §9.2). Values equal names."""
|
|
25
|
+
|
|
26
|
+
MESSAGE_TOO_LONG = "MESSAGE_TOO_LONG"
|
|
27
|
+
AGENT_RUN_ERROR = "AGENT_RUN_ERROR"
|
|
28
|
+
AGENT_HTTP_ERROR = "AGENT_HTTP_ERROR"
|
|
29
|
+
AGENT_STREAM_ERROR = "AGENT_STREAM_ERROR"
|
|
30
|
+
AGENT_EXCEPTION = "AGENT_EXCEPTION"
|
|
31
|
+
INVALID_EDITED_ARGS = "INVALID_EDITED_ARGS"
|
|
32
|
+
SHARED_STATE_LOCKED = "SHARED_STATE_LOCKED"
|
|
33
|
+
SHARED_STATE_INVALID = "SHARED_STATE_INVALID"
|
|
34
|
+
SHARED_STATE_PATCH_FAILED = "SHARED_STATE_PATCH_FAILED"
|
|
35
|
+
CLIENT_TOOL_LOOP_LIMIT = "CLIENT_TOOL_LOOP_LIMIT"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
StreamErrorReason: TypeAlias = Literal["timeout", "eof", "content_type", "connect", "agent_raised"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AgentKitError(Exception):
|
|
42
|
+
"""Base class of every reflex-agentkit exception."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConfigurationError(AgentKitError, ValueError):
|
|
46
|
+
"""Invalid class configuration, tool schema or adapter setup (raised at startup)."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AdapterNotInstalledError(AgentKitError, ImportError):
|
|
50
|
+
"""An adapter was imported without its optional extra installed (API §7.7)."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AgentHTTPError(AgentKitError):
|
|
54
|
+
"""The agent endpoint answered with a non-2xx status."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, status_code: int, message: str | None = None) -> None:
|
|
57
|
+
self.status_code = status_code
|
|
58
|
+
self._message = message
|
|
59
|
+
super().__init__(message or f"agent endpoint returned HTTP {status_code}")
|
|
60
|
+
|
|
61
|
+
def __reduce__(self) -> tuple[Any, ...]:
|
|
62
|
+
return (type(self), (self.status_code, self._message))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class AgentStreamError(AgentKitError):
|
|
66
|
+
"""The event stream failed: timeout, EOF, bad content type, connect, or agent raised."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, reason: StreamErrorReason, message: str | None = None) -> None:
|
|
69
|
+
self.reason: StreamErrorReason = reason
|
|
70
|
+
self._message = message
|
|
71
|
+
super().__init__(message or f"agent stream error: {reason}")
|
|
72
|
+
|
|
73
|
+
def __reduce__(self) -> tuple[Any, ...]:
|
|
74
|
+
return (type(self), (self.reason, self._message))
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""Value types, enums and JSON aliases of the pure core (DM §2, §3, §6, §8; API §5).
|
|
2
|
+
|
|
3
|
+
Every value type is a frozen, slotted dataclass (R-1) whose collections are tuples (R-2)
|
|
4
|
+
and whose enums are ``StrEnum`` (R-3), so the same types serve as Reflex var types (R-5).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import dataclasses
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from enum import StrEnum
|
|
12
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Literal, Self, TypeAlias, Union, cast
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from _typeshed import DataclassInstance
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ChatMessage",
|
|
19
|
+
"CustomEventRecord",
|
|
20
|
+
"Diagnostic",
|
|
21
|
+
"DiagnosticCode",
|
|
22
|
+
"DiagnosticLevel",
|
|
23
|
+
"InterruptAnswer",
|
|
24
|
+
"JsonObject",
|
|
25
|
+
"JsonValue",
|
|
26
|
+
"MessageRole",
|
|
27
|
+
"OpenInterrupt",
|
|
28
|
+
"PendingInterrupt",
|
|
29
|
+
"ReasoningRecord",
|
|
30
|
+
"ReducerConfig",
|
|
31
|
+
"ResumeStatus",
|
|
32
|
+
"RunStatus",
|
|
33
|
+
"TolerantPickle",
|
|
34
|
+
"ToolCall",
|
|
35
|
+
"ToolCallStatus",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# DM R-4. ``Union`` (not ``|``) because the alias is recursive and must stay a runtime value.
|
|
39
|
+
JsonValue: TypeAlias = Union[ # noqa: UP007
|
|
40
|
+
bool, int, float, str, list["JsonValue"], dict[str, "JsonValue"], None
|
|
41
|
+
]
|
|
42
|
+
JsonObject: TypeAlias = dict[str, JsonValue]
|
|
43
|
+
|
|
44
|
+
DiagnosticLevel: TypeAlias = Literal["debug", "warning"]
|
|
45
|
+
ResumeStatus: TypeAlias = Literal["resolved", "cancelled"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MessageRole(StrEnum):
|
|
49
|
+
"""AG-UI message roles (DM §6). Only user/assistant/tool are projected (DM §3.1)."""
|
|
50
|
+
|
|
51
|
+
USER = "user"
|
|
52
|
+
ASSISTANT = "assistant"
|
|
53
|
+
TOOL = "tool"
|
|
54
|
+
SYSTEM = "system"
|
|
55
|
+
DEVELOPER = "developer"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RunStatus(StrEnum):
|
|
59
|
+
"""Run status state machine values (API §4.5)."""
|
|
60
|
+
|
|
61
|
+
IDLE = "idle"
|
|
62
|
+
RUNNING = "running"
|
|
63
|
+
AWAITING_INPUT = "awaiting_input"
|
|
64
|
+
CANCELLED = "cancelled"
|
|
65
|
+
ERROR = "error"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ToolCallStatus(StrEnum):
|
|
69
|
+
"""Tool call status state machine values (API §4.6)."""
|
|
70
|
+
|
|
71
|
+
STREAMING = "streaming"
|
|
72
|
+
PENDING_APPROVAL = "pending_approval"
|
|
73
|
+
RUNNING = "running"
|
|
74
|
+
DONE = "done"
|
|
75
|
+
REJECTED = "rejected"
|
|
76
|
+
ERROR = "error"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class DiagnosticCode(StrEnum):
|
|
80
|
+
"""Codes of non-fatal reducer diagnostics (API §11). Values equal names."""
|
|
81
|
+
|
|
82
|
+
UNKNOWN_EVENT_TYPE = "UNKNOWN_EVENT_TYPE"
|
|
83
|
+
INVALID_EVENT = "INVALID_EVENT"
|
|
84
|
+
IMPLICIT_MESSAGE = "IMPLICIT_MESSAGE"
|
|
85
|
+
UNKNOWN_MESSAGE_ID = "UNKNOWN_MESSAGE_ID"
|
|
86
|
+
UNKNOWN_TOOL_CALL_ID = "UNKNOWN_TOOL_CALL_ID"
|
|
87
|
+
DUPLICATE_ID = "DUPLICATE_ID"
|
|
88
|
+
STALE_RUN_EVENT = "STALE_RUN_EVENT"
|
|
89
|
+
PATCH_FAILED = "PATCH_FAILED"
|
|
90
|
+
ARGS_TOO_LARGE = "ARGS_TOO_LARGE"
|
|
91
|
+
IGNORED_EVENT = "IGNORED_EVENT"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _restore(cls: type[TolerantPickle], state: dict[str, Any]) -> TolerantPickle:
|
|
95
|
+
"""Unpickling entry point: rebuild ``cls`` tolerantly from its pickled state."""
|
|
96
|
+
return cls._from_state(state)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TolerantPickle:
|
|
100
|
+
"""Mixin giving dataclasses a version-tolerant pickle format (DM §8 v1.2).
|
|
101
|
+
|
|
102
|
+
The pickled state is ``{"schema_version": n, **fields}``. Restoring fills missing
|
|
103
|
+
fields with their defaults and drops unknown ones, so an older or newer pickle held
|
|
104
|
+
by the state manager (e.g. Redis) still loads after a library upgrade.
|
|
105
|
+
|
|
106
|
+
It hooks ``__reduce__`` because ``@dataclass(frozen=True, slots=True)`` installs its
|
|
107
|
+
own ``__getstate__``/``__setstate__`` on every class, shadowing a mixin's.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
__slots__ = ()
|
|
111
|
+
|
|
112
|
+
_schema_version: ClassVar[int] = 1
|
|
113
|
+
_derived_fields: ClassVar[frozenset[str]] = frozenset()
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def _persisted_fields(cls) -> tuple[dataclasses.Field[Any], ...]:
|
|
117
|
+
fields = dataclasses.fields(cast("type[DataclassInstance]", cls))
|
|
118
|
+
return tuple(f for f in fields if f.init and f.name not in cls._derived_fields)
|
|
119
|
+
|
|
120
|
+
def _to_state(self) -> dict[str, Any]:
|
|
121
|
+
"""Return the pickled form: the schema version plus every persisted field."""
|
|
122
|
+
state: dict[str, Any] = {"schema_version": self._schema_version}
|
|
123
|
+
for f in self._persisted_fields():
|
|
124
|
+
state[f.name] = getattr(self, f.name)
|
|
125
|
+
return state
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def _from_state(cls, state: dict[str, Any]) -> Self:
|
|
129
|
+
"""Build an instance from a pickled state, tolerating missing and unknown fields."""
|
|
130
|
+
kwargs: dict[str, Any] = {}
|
|
131
|
+
for f in cls._persisted_fields():
|
|
132
|
+
if f.name in state:
|
|
133
|
+
kwargs[f.name] = state[f.name]
|
|
134
|
+
elif f.default is dataclasses.MISSING and f.default_factory is dataclasses.MISSING:
|
|
135
|
+
raise TypeError(
|
|
136
|
+
f"cannot restore {cls.__name__}: required field {f.name!r} is missing"
|
|
137
|
+
)
|
|
138
|
+
return cls(**kwargs)
|
|
139
|
+
|
|
140
|
+
def __reduce__(self) -> tuple[Callable[..., Any], tuple[Any, ...]]:
|
|
141
|
+
return (_restore, (type(self), self._to_state()))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
145
|
+
class ChatMessage(TolerantPickle):
|
|
146
|
+
"""A conversation message (DM §3.1, API §5.1)."""
|
|
147
|
+
|
|
148
|
+
id: str
|
|
149
|
+
role: MessageRole
|
|
150
|
+
content: str = ""
|
|
151
|
+
streaming: bool = False
|
|
152
|
+
tool_call_ids: tuple[str, ...] = ()
|
|
153
|
+
tool_call_id: str | None = None
|
|
154
|
+
reasoning: str = ""
|
|
155
|
+
is_error: bool = False
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
159
|
+
class ToolCall(TolerantPickle):
|
|
160
|
+
"""A tool call issued by the agent (DM §3.2, API §5.2)."""
|
|
161
|
+
|
|
162
|
+
id: str
|
|
163
|
+
name: str
|
|
164
|
+
args_text: str = ""
|
|
165
|
+
args: JsonObject | None = None
|
|
166
|
+
status: ToolCallStatus = ToolCallStatus.STREAMING
|
|
167
|
+
result: str | None = None
|
|
168
|
+
parent_message_id: str | None = None
|
|
169
|
+
interrupt_id: str | None = None
|
|
170
|
+
is_client_tool: bool = False
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
174
|
+
class PendingInterrupt(TolerantPickle):
|
|
175
|
+
"""The generic interrupt shown to the user; computed by ``project()`` (DM §3.3)."""
|
|
176
|
+
|
|
177
|
+
id: str
|
|
178
|
+
reason: str
|
|
179
|
+
message: str
|
|
180
|
+
payload: JsonValue
|
|
181
|
+
response_schema: JsonObject | None
|
|
182
|
+
queue_position: int
|
|
183
|
+
queue_size: int
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
187
|
+
class OpenInterrupt(TolerantPickle):
|
|
188
|
+
"""An interrupt of the current pause, or a local client-tool approval (DM §3.4)."""
|
|
189
|
+
|
|
190
|
+
id: str
|
|
191
|
+
reason: str
|
|
192
|
+
message: str = ""
|
|
193
|
+
tool_call_id: str | None = None
|
|
194
|
+
payload: JsonValue = None
|
|
195
|
+
response_schema: JsonObject | None = None
|
|
196
|
+
is_local: bool = False
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
200
|
+
class InterruptAnswer(TolerantPickle):
|
|
201
|
+
"""The user's answer to an open interrupt, mirroring ``ResumeEntry`` (DM §3.5)."""
|
|
202
|
+
|
|
203
|
+
interrupt_id: str
|
|
204
|
+
status: ResumeStatus
|
|
205
|
+
payload: JsonValue = None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
209
|
+
class ReasoningRecord(TolerantPickle):
|
|
210
|
+
"""Reasoning of one assistant turn (DM §3.6). ``encrypted_value`` is never projected."""
|
|
211
|
+
|
|
212
|
+
id: str
|
|
213
|
+
assistant_message_id: str | None = None
|
|
214
|
+
content: str = ""
|
|
215
|
+
encrypted_value: str | None = None
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
219
|
+
class CustomEventRecord(TolerantPickle):
|
|
220
|
+
"""An AG-UI ``CUSTOM`` event kept in a bounded ring (DM §3.7, API §5.4)."""
|
|
221
|
+
|
|
222
|
+
name: str
|
|
223
|
+
value: JsonValue
|
|
224
|
+
run_id: str
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
228
|
+
class Diagnostic(TolerantPickle):
|
|
229
|
+
"""A transient, non-fatal reducer diagnostic (DM §3.8, API §11)."""
|
|
230
|
+
|
|
231
|
+
level: DiagnosticLevel
|
|
232
|
+
code: DiagnosticCode
|
|
233
|
+
message: str
|
|
234
|
+
event_type: str | None = None
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
238
|
+
class ReducerConfig(TolerantPickle):
|
|
239
|
+
"""Per-run reducer settings built from the ``AgentState`` ClassVars (DM §3.9)."""
|
|
240
|
+
|
|
241
|
+
client_tool_names: frozenset[str] = frozenset()
|
|
242
|
+
custom_events_limit: int = 100
|
|
243
|
+
max_args_bytes: int = 1_048_576
|