alita-sdk 0.3.609__py3-none-any.whl → 0.3.611__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.

Potentially problematic release.


This version of alita-sdk might be problematic. Click here for more details.

@@ -230,18 +230,32 @@ class StateDefaultNode(Runnable):
230
230
  for key, value in self.default_vars.items():
231
231
  if isinstance(value, dict) and 'value' in value:
232
232
  temp_value = value['value']
233
- try:
234
- result[key] = ast.literal_eval(temp_value)
235
- except:
236
- logger.debug("Unable to evaluate value, using as is")
233
+ declared_type = value.get('type', '').lower()
234
+
235
+ # If the declared type is 'str' or 'string', preserve the string value
236
+ # Don't auto-convert even if it looks like a valid Python literal
237
+ if declared_type in ('str', 'string'):
237
238
  result[key] = temp_value
239
+ else:
240
+ # For other types, try to evaluate as Python literal
241
+ try:
242
+ result[key] = ast.literal_eval(temp_value)
243
+ except:
244
+ logger.debug("Unable to evaluate value, using as is")
245
+ result[key] = temp_value
238
246
  return result
239
247
 
240
248
  class PrinterNode(Runnable):
241
249
  name = "PrinterNode"
250
+ DEFAULT_FINAL_MSG = "How to proceed? To resume the pipeline - type anything..."
242
251
 
243
- def __init__(self, input_mapping: Optional[dict[str, dict]]):
252
+ def __init__(self, input_mapping: Optional[dict[str, dict]], final_message: Optional[str] = None):
244
253
  self.input_mapping = input_mapping
254
+ # Apply fallback logic for empty/None values
255
+ if final_message and final_message.strip():
256
+ self.final_message = final_message.strip()
257
+ else:
258
+ self.final_message = self.DEFAULT_FINAL_MSG
245
259
 
246
260
  def invoke(self, state: BaseStore, config: Optional[RunnableConfig] = None) -> dict:
247
261
  logger.info(f"Printer Node - Current state variables: {state}")
@@ -261,7 +275,7 @@ class PrinterNode(Runnable):
261
275
  # convert formatted output to string if it's not
262
276
  if not isinstance(formatted_output, str):
263
277
  formatted_output = str(formatted_output)
264
- formatted_output += f"\n\n-----\n*How to proceed?*\n* *to resume the pipeline - type anything...*"
278
+ formatted_output += f"\n\n-----\n*{self.final_message}*"
265
279
  logger.debug(f"Formatted output: {formatted_output}")
266
280
  result[PRINTER_NODE_RS] = formatted_output
267
281
  return result
@@ -745,6 +759,7 @@ def create_graph(
745
759
  elif node_type == 'printer':
746
760
  lg_builder.add_node(node_id, PrinterNode(
747
761
  input_mapping=node.get('input_mapping', {'printer': {'type': 'fixed', 'value': ''}}),
762
+ final_message=node.get('final_message'),
748
763
  ))
749
764
 
750
765
  # add interrupts after printer node if specified
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import json
2
3
  import logging
3
4
  from copy import deepcopy
@@ -12,6 +13,7 @@ from langchain_core.utils.function_calling import convert_to_openai_tool
12
13
  from pydantic import ValidationError
13
14
 
14
15
  from ..langchain.utils import propagate_the_input_mapping
16
+ from ..utils.serialization import safe_serialize
15
17
 
16
18
  logger = logging.getLogger(__name__)
17
19
 
@@ -40,15 +42,32 @@ class FunctionTool(BaseTool):
40
42
  alita_client: Optional[Any] = None
41
43
 
42
44
  def _prepare_pyodide_input(self, state: Union[str, dict, ToolCall]) -> str:
43
- """Prepare input for PyodideSandboxTool by injecting state into the code block."""
44
- # add state into the code block here since it might be changed during the execution of the code
45
+ """Prepare input for PyodideSandboxTool by injecting state into the code block.
46
+
47
+ Uses base64 encoding to avoid string escaping issues when passing JSON
48
+ through multiple layers of parsing (Python -> Deno -> Pyodide).
49
+ """
45
50
  state_copy = replace_escaped_newlines(deepcopy(state))
46
51
 
47
- del state_copy['messages'] # remove messages to avoid issues with pickling without langchain-core
48
- # inject state into the code block as alita_state variable
49
- state_json = json.dumps(state_copy, ensure_ascii=False)
50
- pyodide_predata = f'#state dict\nimport json\nalita_state = json.loads({json.dumps(state_json)})\n'
52
+ # remove messages to avoid issues with pickling without langchain-core
53
+ if 'messages' in state_copy:
54
+ del state_copy['messages']
55
+
56
+ # Use safe_serialize to handle Pydantic models, datetime, and other non-JSON types
57
+ state_json = safe_serialize(state_copy)
51
58
 
59
+ # Use base64 encoding to avoid all string escaping issues
60
+ # This is more robust than repr() when the code passes through multiple parsers
61
+ state_json_b64 = base64.b64encode(state_json.encode('utf-8')).decode('ascii')
62
+
63
+ # Generate code that decodes base64 and parses JSON inside Pyodide
64
+ pyodide_predata = f'''#state dict
65
+ import json
66
+ import base64
67
+ _state_json_b64 = "{state_json_b64}"
68
+ _state_json = base64.b64decode(_state_json_b64).decode('utf-8')
69
+ alita_state = json.loads(_state_json)
70
+ '''
52
71
  return pyodide_predata
53
72
 
54
73
  def _handle_pyodide_output(self, tool_result: Any) -> dict:
@@ -0,0 +1,155 @@
1
+ """
2
+ Serialization utilities for safe JSON encoding of complex objects.
3
+
4
+ Handles Pydantic models, LangChain messages, datetime objects, and other
5
+ non-standard types that may appear in state variables.
6
+ """
7
+ import json
8
+ import logging
9
+ from datetime import datetime, date
10
+ from typing import Any
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def _convert_to_serializable(obj: Any, _seen: set = None) -> Any:
16
+ """
17
+ Recursively convert an object to JSON-serializable primitives.
18
+
19
+ Handles nested dicts and lists that may contain non-serializable objects.
20
+ Uses a seen set to prevent infinite recursion with circular references.
21
+
22
+ Args:
23
+ obj: Any object to convert
24
+ _seen: Internal set to track seen object ids (for circular reference detection)
25
+
26
+ Returns:
27
+ JSON-serializable representation of the object
28
+ """
29
+ # Initialize seen set for circular reference detection
30
+ if _seen is None:
31
+ _seen = set()
32
+
33
+ # Check for circular references (only for mutable objects)
34
+ obj_id = id(obj)
35
+ if isinstance(obj, (dict, list, set)) and obj_id in _seen:
36
+ return f"<circular reference: {type(obj).__name__}>"
37
+
38
+ # Primitives - return as-is
39
+ if obj is None or isinstance(obj, (str, int, float, bool)):
40
+ return obj
41
+
42
+ # Add to seen set for mutable containers
43
+ if isinstance(obj, (dict, list, set)):
44
+ _seen = _seen | {obj_id} # Create new set to avoid mutation issues
45
+
46
+ # Dict - recursively process all values
47
+ if isinstance(obj, dict):
48
+ return {
49
+ _convert_to_serializable(k, _seen): _convert_to_serializable(v, _seen)
50
+ for k, v in obj.items()
51
+ }
52
+
53
+ # List/tuple - recursively process all items
54
+ if isinstance(obj, (list, tuple)):
55
+ return [_convert_to_serializable(item, _seen) for item in obj]
56
+
57
+ # Set - convert to list and process
58
+ if isinstance(obj, set):
59
+ return [_convert_to_serializable(item, _seen) for item in obj]
60
+
61
+ # Bytes - decode to string
62
+ if isinstance(obj, bytes):
63
+ try:
64
+ return obj.decode('utf-8')
65
+ except UnicodeDecodeError:
66
+ return obj.decode('utf-8', errors='replace')
67
+
68
+ # Datetime objects
69
+ if isinstance(obj, datetime):
70
+ return obj.isoformat()
71
+ if isinstance(obj, date):
72
+ return obj.isoformat()
73
+
74
+ # Pydantic BaseModel (v2) - check for model_dump method
75
+ if hasattr(obj, 'model_dump') and callable(getattr(obj, 'model_dump')):
76
+ try:
77
+ return _convert_to_serializable(obj.model_dump(), _seen)
78
+ except Exception as e:
79
+ logger.debug(f"Failed to call model_dump on {type(obj).__name__}: {e}")
80
+
81
+ # Pydantic BaseModel (v1) - check for dict method
82
+ if hasattr(obj, 'dict') and callable(getattr(obj, 'dict')) and hasattr(obj, '__fields__'):
83
+ try:
84
+ return _convert_to_serializable(obj.dict(), _seen)
85
+ except Exception as e:
86
+ logger.debug(f"Failed to call dict on {type(obj).__name__}: {e}")
87
+
88
+ # LangChain BaseMessage - extract key fields
89
+ if hasattr(obj, 'type') and hasattr(obj, 'content'):
90
+ try:
91
+ result = {
92
+ "type": obj.type,
93
+ "content": _convert_to_serializable(obj.content, _seen),
94
+ }
95
+ if hasattr(obj, 'additional_kwargs') and obj.additional_kwargs:
96
+ result["additional_kwargs"] = _convert_to_serializable(obj.additional_kwargs, _seen)
97
+ if hasattr(obj, 'name') and obj.name:
98
+ result["name"] = obj.name
99
+ return result
100
+ except Exception as e:
101
+ logger.debug(f"Failed to extract message fields from {type(obj).__name__}: {e}")
102
+
103
+ # Objects with __dict__ attribute (custom classes)
104
+ if hasattr(obj, '__dict__'):
105
+ try:
106
+ return _convert_to_serializable(obj.__dict__, _seen)
107
+ except Exception as e:
108
+ logger.debug(f"Failed to serialize __dict__ of {type(obj).__name__}: {e}")
109
+
110
+ # UUID objects
111
+ if hasattr(obj, 'hex') and hasattr(obj, 'int'):
112
+ return str(obj)
113
+
114
+ # Enum objects
115
+ if hasattr(obj, 'value') and hasattr(obj, 'name') and hasattr(obj.__class__, '__members__'):
116
+ return obj.value
117
+
118
+ # Last resort - convert to string
119
+ try:
120
+ return str(obj)
121
+ except Exception:
122
+ return f"<non-serializable: {type(obj).__name__}>"
123
+
124
+
125
+ def safe_serialize(obj: Any, **kwargs) -> str:
126
+ """
127
+ Safely serialize any object to a JSON string.
128
+
129
+ Pre-processes the entire object tree to convert non-serializable
130
+ objects before passing to json.dumps. This ensures nested dicts
131
+ and lists with non-standard objects are handled correctly.
132
+
133
+ Args:
134
+ obj: Any object to serialize
135
+ **kwargs: Additional arguments passed to json.dumps
136
+ (e.g., indent, sort_keys)
137
+
138
+ Returns:
139
+ JSON string representation of the object
140
+
141
+ Example:
142
+ >>> from pydantic import BaseModel
143
+ >>> class User(BaseModel):
144
+ ... name: str
145
+ >>> state = {"user": User(name="Alice"), "count": 5}
146
+ >>> safe_serialize(state)
147
+ '{"user": {"name": "Alice"}, "count": 5}'
148
+ """
149
+ # Pre-process the entire object tree
150
+ serializable = _convert_to_serializable(obj)
151
+
152
+ # Set defaults
153
+ kwargs.setdefault('ensure_ascii', False)
154
+
155
+ return json.dumps(serializable, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.609
3
+ Version: 0.3.611
4
4
  Summary: SDK for building langchain agents using resources from Alita
5
5
  Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedj27@gmail.com>, Artem Dubrovskiy <ad13box@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -119,7 +119,7 @@ alita_sdk/runtime/langchain/assistant.py,sha256=RcU82QyxtLW8yWeamEBqYJ6B243ehRya
119
119
  alita_sdk/runtime/langchain/chat_message_template.py,sha256=kPz8W2BG6IMyITFDA5oeb5BxVRkHEVZhuiGl4MBZKdc,2176
120
120
  alita_sdk/runtime/langchain/constants.py,sha256=GxZP0dupwaVq0iTKE2nNF-Vp0wzMlkh_aiBAhCuy84E,29451
121
121
  alita_sdk/runtime/langchain/indexer.py,sha256=0ENHy5EOhThnAiYFc7QAsaTNp9rr8hDV_hTK8ahbatk,37592
122
- alita_sdk/runtime/langchain/langraph_agent.py,sha256=-cMdn3LdCImV82HiSQdgb6f7jA4LeEWjTso9f-KHsng,63140
122
+ alita_sdk/runtime/langchain/langraph_agent.py,sha256=7mkOpl_cBCb6hZqNCN-B9TePRQjnblLMWDOzcGni-rY,63957
123
123
  alita_sdk/runtime/langchain/mixedAgentParser.py,sha256=M256lvtsL3YtYflBCEp-rWKrKtcY1dJIyRGVv7KW9ME,2611
124
124
  alita_sdk/runtime/langchain/mixedAgentRenderes.py,sha256=asBtKqm88QhZRILditjYICwFVKF5KfO38hu2O-WrSWE,5964
125
125
  alita_sdk/runtime/langchain/store_manager.py,sha256=i8Fl11IXJhrBXq1F1ukEVln57B1IBe-tqSUvfUmBV4A,2218
@@ -199,7 +199,7 @@ alita_sdk/runtime/tools/artifact.py,sha256=Us-NM1VTfqDNBzs8nMsI3Inu-_V9SoZOqjfUA
199
199
  alita_sdk/runtime/tools/data_analysis.py,sha256=PHQ0xa2eDkw6FsHAHVTWB58wO8tg76tHrp4lXRQZ0jQ,6396
200
200
  alita_sdk/runtime/tools/datasource.py,sha256=pvbaSfI-ThQQnjHG-QhYNSTYRnZB0rYtZFpjCfpzxYI,2443
201
201
  alita_sdk/runtime/tools/echo.py,sha256=spw9eCweXzixJqHnZofHE1yWiSUa04L4VKycf3KCEaM,486
202
- alita_sdk/runtime/tools/function.py,sha256=HSMO1nBTRKMvWC_m0M8TOLGaZ2k_7ksPgLqzuRh6kV4,7083
202
+ alita_sdk/runtime/tools/function.py,sha256=x5gaaCrxgA5vwq29pUpAgGA5l6THriOJx9d58-G9Ybo,7679
203
203
  alita_sdk/runtime/tools/graph.py,sha256=7jImBBSEdP5Mjnn2keOiyUwdGDFhEXLUrgUiugO3mgA,3503
204
204
  alita_sdk/runtime/tools/image_generation.py,sha256=waxxFIAgmh9-COcljL9uZ7e_s7EL9OWveUxYk0ulEUM,7855
205
205
  alita_sdk/runtime/tools/indexer_tool.py,sha256=whSLPevB4WD6dhh2JDXEivDmTvbjiMV1MrPl9cz5eLA,4375
@@ -230,6 +230,7 @@ alita_sdk/runtime/utils/mcp_oauth.py,sha256=YfMFZOs1nGSR7ZvGKSE6nKlpJRNkr2h0BZXk
230
230
  alita_sdk/runtime/utils/mcp_sse_client.py,sha256=wU_fmZbh2sXUYjkJmBjmpfhFLpncjAXE5Wg3fqyNhmk,18318
231
231
  alita_sdk/runtime/utils/mcp_tools_discovery.py,sha256=pL1bMUEaZmiABFGsphed0LvhR-wGfkNnCpD_89R01Ws,3908
232
232
  alita_sdk/runtime/utils/save_dataframe.py,sha256=i-E1wp-t4wb17Zq3nA3xYwgSILjoXNizaQAA9opWvxY,1576
233
+ alita_sdk/runtime/utils/serialization.py,sha256=J7cQS7-SYD8haUv5Inn44EfsTzmAzOquacgn_t2GU-g,5327
233
234
  alita_sdk/runtime/utils/streamlit.py,sha256=0TotNKnvMPHuwBdhMEpM5DhIedQQa1AUz9BlmXFBhAU,107179
234
235
  alita_sdk/runtime/utils/toolkit_runtime.py,sha256=MU63Fpxj0b5_r1IUUc0Q3-PN9VwL7rUxp2MRR4tmYR8,5136
235
236
  alita_sdk/runtime/utils/toolkit_utils.py,sha256=c56a9jzlOXYYdl67N3arGl5sOqO3ly60bne2OBYuQKc,6758
@@ -458,9 +459,9 @@ alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=ppJayzkKRhTbQVVd2EhQmvADwdosl
458
459
  alita_sdk/tools/zephyr_squad/__init__.py,sha256=cUSc0ZhpGmWnTQ3ZjllU9QmNlCfaHZ21HCFjfenSMH8,3081
459
460
  alita_sdk/tools/zephyr_squad/api_wrapper.py,sha256=kmw_xol8YIYFplBLWTqP_VKPRhL_1ItDD0_vXTe_UuI,14906
460
461
  alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py,sha256=R371waHsms4sllHCbijKYs90C-9Yu0sSR3N4SUfQOgU,5066
461
- alita_sdk-0.3.609.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
462
- alita_sdk-0.3.609.dist-info/METADATA,sha256=hEWxQjw26WUFFy2f56DL-k9cuqD3Xxxszb6f4YEFtC8,24339
463
- alita_sdk-0.3.609.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
464
- alita_sdk-0.3.609.dist-info/entry_points.txt,sha256=VijN0h4alp1WXm8tfS3P7vuGxN4a5RZqHjXAoEIBZnI,49
465
- alita_sdk-0.3.609.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
466
- alita_sdk-0.3.609.dist-info/RECORD,,
462
+ alita_sdk-0.3.611.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
463
+ alita_sdk-0.3.611.dist-info/METADATA,sha256=VbmEWybS3DmRjXF8BIB5vG8dLgO5HS7Vm9_qbMG7miQ,24339
464
+ alita_sdk-0.3.611.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
465
+ alita_sdk-0.3.611.dist-info/entry_points.txt,sha256=VijN0h4alp1WXm8tfS3P7vuGxN4a5RZqHjXAoEIBZnI,49
466
+ alita_sdk-0.3.611.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
467
+ alita_sdk-0.3.611.dist-info/RECORD,,