agent-lab-sdk 0.1.35__py3-none-any.whl → 0.1.49__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.
@@ -0,0 +1,172 @@
1
+ import asyncio
2
+ import re
3
+ import uuid
4
+ from base64 import b64encode
5
+ from collections import deque
6
+ from collections.abc import Mapping
7
+ from datetime import timedelta, timezone
8
+ from decimal import Decimal
9
+ from ipaddress import (
10
+ IPv4Address,
11
+ IPv4Interface,
12
+ IPv4Network,
13
+ IPv6Address,
14
+ IPv6Interface,
15
+ IPv6Network,
16
+ )
17
+ from pathlib import Path
18
+ from re import Pattern
19
+ from typing import Any, NamedTuple
20
+ from zoneinfo import ZoneInfo
21
+
22
+ import cloudpickle
23
+ import orjson
24
+ import logging
25
+ from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class Fragment(NamedTuple):
31
+ buf: bytes
32
+
33
+
34
+ def decimal_encoder(dec_value: Decimal) -> int | float:
35
+ """
36
+ Encodes a Decimal as int of there's no exponent, otherwise float
37
+
38
+ This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
39
+ where a integer (but not int typed) is used. Encoding this as a float
40
+ results in failed round-tripping between encode and parse.
41
+ Our Id type is a prime example of this.
42
+
43
+ >>> decimal_encoder(Decimal("1.0"))
44
+ 1.0
45
+
46
+ >>> decimal_encoder(Decimal("1"))
47
+ 1
48
+ """
49
+ if dec_value.as_tuple().exponent >= 0:
50
+ return int(dec_value)
51
+ else:
52
+ return float(dec_value)
53
+
54
+
55
+ def default(obj):
56
+ # Only need to handle types that orjson doesn't serialize by default
57
+ # https://github.com/ijl/orjson#serialize
58
+ if isinstance(obj, Fragment):
59
+ return orjson.Fragment(obj.buf)
60
+ if (
61
+ hasattr(obj, "model_dump")
62
+ and callable(obj.model_dump)
63
+ and not isinstance(obj, type)
64
+ ):
65
+ return obj.model_dump()
66
+ elif hasattr(obj, "dict") and callable(obj.dict) and not isinstance(obj, type):
67
+ return obj.dict()
68
+ elif (
69
+ hasattr(obj, "_asdict") and callable(obj._asdict) and not isinstance(obj, type)
70
+ ):
71
+ return obj._asdict()
72
+ elif isinstance(obj, BaseException):
73
+ return {"error": type(obj).__name__, "message": str(obj)}
74
+ elif isinstance(obj, (set, frozenset, deque)): # noqa: UP038
75
+ return list(obj)
76
+ elif isinstance(obj, (timezone, ZoneInfo)): # noqa: UP038
77
+ return obj.tzname(None)
78
+ elif isinstance(obj, timedelta):
79
+ return obj.total_seconds()
80
+ elif isinstance(obj, Decimal):
81
+ return decimal_encoder(obj)
82
+ elif isinstance(obj, uuid.UUID):
83
+ return str(obj)
84
+ elif isinstance( # noqa: UP038
85
+ obj,
86
+ (
87
+ IPv4Address,
88
+ IPv4Interface,
89
+ IPv4Network,
90
+ IPv6Address,
91
+ IPv6Interface,
92
+ IPv6Network,
93
+ Path,
94
+ ),
95
+ ):
96
+ return str(obj)
97
+ elif isinstance(obj, Pattern):
98
+ return obj.pattern
99
+ elif isinstance(obj, bytes | bytearray):
100
+ return b64encode(obj).decode()
101
+ return None
102
+
103
+
104
+ _option = orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS
105
+
106
+ _SURROGATE_RE = re.compile(r"[\ud800-\udfff]")
107
+
108
+
109
+ def _strip_surr(s: str) -> str:
110
+ return s if _SURROGATE_RE.search(s) is None else _SURROGATE_RE.sub("", s)
111
+
112
+
113
+ def _sanitise(o: Any) -> Any:
114
+ if isinstance(o, str):
115
+ return _strip_surr(o)
116
+ if isinstance(o, Mapping):
117
+ return {_sanitise(k): _sanitise(v) for k, v in o.items()}
118
+ if isinstance(o, list | tuple | set):
119
+ ctor = list if isinstance(o, list) else type(o)
120
+ return ctor(_sanitise(x) for x in o)
121
+ return o
122
+
123
+
124
+ def json_dumpb(obj) -> bytes:
125
+ try:
126
+ dumped = orjson.dumps(obj, default=default, option=_option)
127
+ except TypeError as e:
128
+ if "surrogates not allowed" not in str(e):
129
+ raise
130
+ dumped = orjson.dumps(_sanitise(obj), default=default, option=_option)
131
+ return (
132
+ # Unfortunately simply doing ``.replace(rb"\\u0000", b"")`` on
133
+ # the dumped bytes can leave an **orphaned back-slash** (e.g. ``\\q``)
134
+ # which makes the resulting JSON invalid. The fix is to delete the *double*
135
+ # back-slash form **first**, then (optionally) the single-escapes.
136
+ dumped.replace(rb"\\u0000", b"").replace(rb"\u0000", b"")
137
+ )
138
+
139
+
140
+ def json_loads(content: bytes | Fragment | dict) -> Any:
141
+ if isinstance(content, Fragment):
142
+ content = content.buf
143
+ if isinstance(content, dict):
144
+ return content
145
+ return orjson.loads(content)
146
+
147
+
148
+ async def ajson_loads(content: bytes | Fragment) -> Any:
149
+ return await asyncio.to_thread(json_loads, content)
150
+
151
+
152
+ class Serializer(JsonPlusSerializer):
153
+ def dumps_typed(self, obj: Any) -> tuple[str, bytes]:
154
+ try:
155
+ return super().dumps_typed(obj)
156
+ except TypeError:
157
+ return "pickle", cloudpickle.dumps(obj)
158
+
159
+ def dumps(self, obj: Any) -> bytes:
160
+ # See comment above (in json_dumpb)
161
+ return super().dumps(obj).replace(rb"\\u0000", b"").replace(rb"\u0000", b"")
162
+
163
+ def loads_typed(self, data: tuple[str, bytes]) -> Any:
164
+ if data[0] == "pickle":
165
+ try:
166
+ return cloudpickle.loads(data[1])
167
+ except Exception as e:
168
+ logger.warning(
169
+ "Failed to unpickle object, replacing w None", exc_info=e
170
+ )
171
+ return None
172
+ return super().loads_typed(data)
agent_lab_sdk/llm/llm.py CHANGED
@@ -38,4 +38,7 @@ def get_model(
38
38
  # Включаем режим авто-обновления токена в обёртках
39
39
  kwargs["manage_access_token"] = True
40
40
 
41
- return _class(verify_ssl_certs=verify_ssl_certs, **kwargs)
41
+ if access_token:
42
+ kwargs["access_token"] = access_token
43
+
44
+ return _class(verify_ssl_certs=verify_ssl_certs, **kwargs)
@@ -10,13 +10,14 @@ from .input_types import (
10
10
  SwitchInput,
11
11
  FileInput,
12
12
  FilesInput,
13
- SelectOption
13
+ SelectOption,
14
+ Visibility
14
15
  )
15
16
 
16
17
  __all__ = [
17
18
  "LogMessage",
18
19
  "MainInput",
19
- "StringInput",
20
+ "StringInput",
20
21
  "StringArrayInput",
21
22
  "StringArrayInputInline",
22
23
  "NumberInput",
@@ -25,5 +26,6 @@ __all__ = [
25
26
  "SwitchInput",
26
27
  "FileInput",
27
28
  "FilesInput",
28
- "SelectOption"
29
+ "SelectOption",
30
+ "Visibility"
29
31
  ]
@@ -1,52 +1,77 @@
1
- from typing import List, Optional, Any
2
- from pydantic import BaseModel
3
- from pydantic.json_schema import WithJsonSchema
1
+ from typing import List, Optional, Any, Literal
2
+ from enum import Enum
3
+ from pydantic import BaseModel, Field
4
+ import os
4
5
 
6
+ def _as_bool(s: str | None) -> bool:
7
+ return (s or "").lower() in {"1", "true", "yes", "on"}
5
8
 
6
- def MainInput(placeholder: str | None = None) -> type:
9
+ UI_TYPE_KEY = "inputType" if _as_bool(os.getenv("USE_UI_TYPE_INSTEAD_OF_TYPE", "False")) else "type"
10
+
11
+ class Visibility(str, Enum):
12
+ """
13
+ Enum representing visibility states for input fields.
14
+
15
+ Attributes:
16
+ ALWAYS: Field is always visible
17
+ START: Field is visible only at start
18
+ AFTER_START: Field is visible after start
19
+ """
20
+ ALWAYS = "always"
21
+ START = "start"
22
+ AFTER_START = "after_start"
23
+
24
+
25
+ def MainInput(placeholder: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
7
26
  """
8
27
  Factory function for creating a main input type with optional placeholder.
9
-
28
+
10
29
  Args:
11
30
  placeholder: Optional placeholder text for the input field
12
-
31
+ visibility: Visibility state of the field (default: "always")
32
+
13
33
  Returns:
14
34
  Type annotation for main input field
15
35
  """
16
- return WithJsonSchema({
17
- "type": "main-input",
36
+ return Field(json_schema_extra={
37
+ UI_TYPE_KEY: "main-input",
18
38
  "placeholder": placeholder,
39
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
19
40
  })
20
41
 
21
42
 
22
- def StringInput(default: str | None = None, title: str | None = None, description: str | None = None, hidden: bool | None = False, depends: str | None = None) -> type:
43
+ def StringInput(default: str | None = None, title: str | None = None, description: str | None = None,
44
+ hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
23
45
  """
24
46
  Factory function for creating a string input type.
25
-
47
+
26
48
  Args:
27
49
  default: Default value for the string input
28
50
  title: Title for the string input
29
51
  description: Description text for the string input
30
52
  hidden: Whether the input should be hidden in the UI
31
53
  depends: Specifies the parameter that this value depends on or is derived from.
32
-
54
+ visibility: Visibility state of the field (default: "always")
55
+
33
56
  Returns:
34
57
  Type annotation for string input field
35
58
  """
36
- return WithJsonSchema({
37
- "type": "input-string",
59
+ return Field(json_schema_extra={
60
+ UI_TYPE_KEY: "input-string",
38
61
  "default": default,
39
62
  "title": title,
40
63
  "description": description,
41
64
  "hidden": hidden,
42
65
  "depends": depends,
66
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
43
67
  })
44
68
 
45
69
 
46
- def StringArrayInput(placeholder: str | None = None, title: str | None = None, description: str | None = None, group: str | None = None, hidden: bool | None = False, depends: str | None = None) -> type:
70
+ def StringArrayInput(placeholder: str | None = None, title: str | None = None, description: str | None = None,
71
+ group: str | None = None, hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
47
72
  """
48
73
  Factory function for creating a string array input type.
49
-
74
+
50
75
  Args:
51
76
  placeholder: Placeholder text for the input field
52
77
  title: Title for the string array input
@@ -54,23 +79,25 @@ def StringArrayInput(placeholder: str | None = None, title: str | None = None, d
54
79
  group: Group name for organizing inputs in the UI
55
80
  hidden: Whether the input should be hidden in the UI
56
81
  depends: Specifies the parameter that this value depends on or is derived from.
57
-
82
+ visibility: Visibility state of the field (default: "always")
83
+
58
84
  Returns:
59
85
  Type annotation for string array input field
60
86
  """
61
- return WithJsonSchema({
62
- "type": "string[]",
87
+ return Field(json_schema_extra={
88
+ UI_TYPE_KEY: "string[]",
63
89
  "placeholder": placeholder,
64
90
  "title": title,
65
91
  "description": description,
66
92
  "group": group,
67
93
  "hidden": hidden,
68
94
  "depends": depends,
95
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
69
96
  })
70
97
 
71
98
 
72
99
  def StringArrayInputInline(placeholder: str | None = None, title: str | None = None, description: str | None = None,
73
- group: str | None = None, hidden: bool | None = False, depends: str | None = None) -> type:
100
+ group: str | None = None, hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
74
101
  """
75
102
  Factory function for creating a string array input inline type.
76
103
 
@@ -81,42 +108,47 @@ def StringArrayInputInline(placeholder: str | None = None, title: str | None = N
81
108
  group: Group name for organizing inputs in the UI
82
109
  hidden: Whether the input should be hidden in the UI
83
110
  depends: Specifies the parameter that this value depends on or is derived from.
111
+ visibility: Visibility state of the field (default: "always")
84
112
 
85
113
  Returns:
86
114
  Type annotation for string array input field
87
115
  """
88
- return WithJsonSchema({
89
- "type": "string[]-inline",
116
+ return Field(json_schema_extra={
117
+ UI_TYPE_KEY: "string[]-inline",
90
118
  "placeholder": placeholder,
91
119
  "title": title,
92
120
  "description": description,
93
121
  "group": group,
94
122
  "hidden": hidden,
95
123
  "depends": depends,
124
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
96
125
  })
97
126
 
98
127
 
99
- def NumberInput(default: float | None = None, title: str | None = None, description: str | None = None, hidden: bool | None = False, depends: str | None = None) -> type:
128
+ def NumberInput(default: float | None = None, title: str | None = None, description: str | None = None,
129
+ hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
100
130
  """
101
131
  Factory function for creating a number input type.
102
-
132
+
103
133
  Args:
104
134
  default: Default value for the number input
105
135
  title: Title for the number input
106
136
  description: Description text for the number input
107
137
  hidden: Whether the input should be hidden in the UI
108
138
  depends: Specifies the parameter that this value depends on or is derived from.
109
-
139
+ visibility: Visibility state of the field (default: "always")
140
+
110
141
  Returns:
111
142
  Type annotation for number input field
112
143
  """
113
- return WithJsonSchema({
114
- "type": "input-number",
144
+ return Field(json_schema_extra={
145
+ UI_TYPE_KEY: "input-number",
115
146
  "default": default,
116
147
  "title": title,
117
148
  "description": description,
118
149
  "hidden": hidden,
119
150
  "depends": depends,
151
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
120
152
  })
121
153
 
122
154
 
@@ -126,7 +158,7 @@ class SelectOption(BaseModel):
126
158
 
127
159
  Attributes:
128
160
  label: Display label for the option
129
- value: Actual value of the option
161
+ value: Actual value for the option
130
162
  description: Optional description for the option
131
163
  """
132
164
  label: str
@@ -134,10 +166,11 @@ class SelectOption(BaseModel):
134
166
  description: Optional[str] = None
135
167
 
136
168
 
137
- def SelectInput(items: List[Any] = [], title: str | None = None, group: str | None = None, default: str | None = None, hidden: bool | None = False, depends: str | None = None) -> type:
169
+ def SelectInput(items: List[Any] = [], title: str | None = None, group: str | None = None, default: str | None = None,
170
+ hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
138
171
  """
139
172
  Factory function for creating a select input type.
140
-
173
+
141
174
  Args:
142
175
  items: List of SelectOption objects or dictionaries
143
176
  title: Title for the select input
@@ -145,25 +178,28 @@ def SelectInput(items: List[Any] = [], title: str | None = None, group: str | No
145
178
  default: Default selected value
146
179
  hidden: Whether the input should be hidden in the UI
147
180
  depends: Specifies the parameter that this value depends on or is derived from.
148
-
181
+ visibility: Visibility state of the field (default: "always")
182
+
149
183
  Returns:
150
184
  Type annotation for select input field
151
185
  """
152
- return WithJsonSchema({
153
- "type": "select",
186
+ return Field(json_schema_extra={
187
+ UI_TYPE_KEY: "select",
154
188
  "title": title,
155
189
  "items": items,
156
190
  "group": group,
157
191
  "default": default,
158
192
  "hidden": hidden,
159
193
  "depends": depends,
194
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
160
195
  })
161
196
 
162
197
 
163
- def CheckboxInput(title: str | None = None, group: str | None = None, description: str | None = None, default: bool | None = False, hidden: bool | None = False, depends: str | None = None) -> type:
198
+ def CheckboxInput(title: str | None = None, group: str | None = None, description: str | None = None,
199
+ default: bool | None = False, hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
164
200
  """
165
201
  Factory function for creating a checkbox input type.
166
-
202
+
167
203
  Args:
168
204
  title: Title for the checkbox
169
205
  group: Group name for organizing inputs in the UI
@@ -171,23 +207,25 @@ def CheckboxInput(title: str | None = None, group: str | None = None, descriptio
171
207
  default: Default checked state
172
208
  hidden: Whether the input should be hidden in the UI
173
209
  depends: Specifies the parameter that this value depends on or is derived from.
174
-
210
+ visibility: Visibility state of the field (default: "always")
211
+
175
212
  Returns:
176
213
  Type annotation for checkbox input field
177
214
  """
178
- return WithJsonSchema({
179
- "type": "checkbox",
215
+ return Field(json_schema_extra={
216
+ UI_TYPE_KEY: "checkbox",
180
217
  "title": title,
181
218
  "group": group,
182
219
  "description": description,
183
220
  "default": default,
184
221
  "hidden": hidden,
185
222
  "depends": depends,
223
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
186
224
  })
187
225
 
188
226
 
189
227
  def SwitchInput(title: str | None = None, group: str | None = None, description: str | None = None,
190
- default: bool | None = False, hidden: bool | None = False, depends: str | None = None) -> type:
228
+ default: bool | None = False, hidden: bool | None = False, depends: str | None = None, visibility: str | Visibility = Visibility.ALWAYS) -> type:
191
229
  """
192
230
  Factory function for creating a switch input type.
193
231
 
@@ -198,50 +236,60 @@ def SwitchInput(title: str | None = None, group: str | None = None, description:
198
236
  default: Default checked state
199
237
  hidden: Whether the input should be hidden in the UI
200
238
  depends: Specifies the parameter that this value depends on or is derived from.
239
+ visibility: Visibility state of the field (default: "always")
201
240
 
202
241
  Returns:
203
242
  Type annotation for switch input field
204
243
  """
205
- return WithJsonSchema({
206
- "type": "switch",
244
+ return Field(json_schema_extra={
245
+ UI_TYPE_KEY: "switch",
207
246
  "title": title,
208
247
  "group": group,
209
248
  "description": description,
210
249
  "default": default,
211
250
  "hidden": hidden,
212
251
  "depends": depends,
252
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
213
253
  })
214
254
 
215
255
 
216
- def FileInput(title: str | None = None, file_extensions: str | None = None, group: str | None = None, hidden: bool | None = False, depends: str | None = None) -> type:
256
+ def FileInput(title: str | None = None, file_extensions: str | None = None, group: str | None = None,
257
+ hidden: bool | None = False, depends: str | None = None, view: Literal["button", "dropzone"] | None = None,
258
+ visibility: str | Visibility = Visibility.ALWAYS, max_size_mb: float | None = 100.0) -> type:
217
259
  """
218
260
  Factory function for creating a single file input type.
219
-
261
+
220
262
  Args:
221
263
  title: Title for the file input
222
264
  file_extensions: Comma-separated list of allowed file extensions (e.g., ".pdf,.txt")
223
265
  group: Group name for organizing inputs in the UI
224
266
  hidden: Whether the input should be hidden in the UI
225
267
  depends: Specifies the parameter that this value depends on or is derived from.
226
-
268
+ view: View mode for the file input ("button" or "dropzone")
269
+ visibility: Visibility state of the field (default: "always")
270
+
227
271
  Returns:
228
272
  Type annotation for file input field
229
273
  """
230
- return WithJsonSchema({
231
- "type": "file",
274
+ return Field(json_schema_extra={
275
+ UI_TYPE_KEY: "file",
232
276
  "title": title,
233
277
  "fileExtensions": file_extensions,
234
278
  "group": group,
235
279
  "hidden": hidden,
236
280
  "depends": depends,
281
+ "view": view,
282
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
283
+ "maxSizeMB": max_size_mb,
237
284
  })
238
285
 
239
286
 
240
287
  def FilesInput(title: str | None = None, file_extensions: str | None = None, group: str | None = None,
241
- hidden: bool | None = False, depends: str | None = None, limit: int | None = 10) -> type:
288
+ hidden: bool | None = False, depends: str | None = None, limit: int | None = 10,
289
+ view: Literal["button", "dropzone"] | None = None, visibility: str | Visibility = Visibility.ALWAYS, max_size_mb: float | None = 100.0) -> type:
242
290
  """
243
291
  Factory function for creating a multiple files input type.
244
-
292
+
245
293
  Args:
246
294
  title: Title for the files input
247
295
  file_extensions: Comma-separated list of allowed file extensions (e.g., ".pdf,.txt")
@@ -249,16 +297,21 @@ def FilesInput(title: str | None = None, file_extensions: str | None = None, gro
249
297
  hidden: Whether the input should be hidden in the UI
250
298
  depends: Specifies the parameter that this value depends on or is derived from.
251
299
  limit: Limit count files.
300
+ view: View mode for the files input ("button" or "dropzone")
301
+ visibility: Visibility state of the field (default: "always")
252
302
 
253
303
  Returns:
254
304
  Type annotation for files input field
255
305
  """
256
- return WithJsonSchema({
257
- "type": "files",
306
+ return Field(json_schema_extra={
307
+ UI_TYPE_KEY: "files",
258
308
  "title": title,
259
309
  "fileExtensions": file_extensions,
260
310
  "group": group,
261
311
  "hidden": hidden,
262
312
  "depends": depends,
263
313
  "limit": limit,
264
- })
314
+ "view": view,
315
+ "visibility": visibility.value if isinstance(visibility, Visibility) else visibility,
316
+ "maxSizeMB": max_size_mb,
317
+ })
@@ -1,3 +1,4 @@
1
1
  from .storage import store_file_in_sd_asset
2
+ from .storage_v2 import upload_file, FileUploadResponse
2
3
 
3
- __all__ = ["store_file_in_sd_asset"]
4
+ __all__ = ["store_file_in_sd_asset", "upload_file", "FileUploadResponse"]