ToolMessage artifact failed to save in memory #156

Closed
opened 2026-02-20 17:29:44 -05:00 by yindo · 6 comments
Owner

Originally created by @HaoXuAI on GitHub (Jul 22, 2024).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph/LangChain documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph/LangChain rather than my code.
  • I am sure this is better as an issue rather than a GitHub discussion, since this is a LangGraph bug and not a design question.

Example Code

(content='success', name='sql_database_query', id='e419a154-04ef-4df5-b04a-e3e6961abb81', tool_call_id='call_bD8KUN0fUAVx4BmcONICNVnv', artifact=[('Customer#000143500', Decimal('7012696.48')), ('Customer#000095257', Decimal('6563511.23')), ('Customer#000087115', Decimal('6457526.26')), ('Customer#000131113', Decimal('6311428.86')), ('Customer#000103834', Decimal('6306524.23'))])``

Error Message and Stack Trace (if applicable)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 sql_agent.stream("I want to get the top 5 customers which making the most purchases", tool_mode=ToolMode.CONTINUOUS, display=True)

File ~/dev/aita/aita/aita/agent/graph.py:171, in GraphAgent.stream(self, question, tool_mode, max_iterations, display)
    169         if display:
    170             print(f"Iteration {iterations}")
--> 171             self._print_graph_events(events, _printed)
    172         current_state = self.get_current_state()
    173 if self.output_parser:

File ~/dev/aita/aita/aita/agent/graph.py:257, in GraphAgent._print_graph_events(self, events, printed, max_length)
    255 if isinstance(events, dict):
    256     events = [events]
--> 257 for event in events:
    258     messages = self._get_event_message(event)
    259     for message in messages:

File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/pregel/__init__.py:884, in Pregel.stream(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug)
    882 start = saved.metadata.get("step", -2) + 1 if saved else -1
    883 # create channels from checkpoint
--> 884 with BackgroundExecutor(config) as submit, ChannelsManager(
    885     self.channels, checkpoint, config
    886 ) as channels, ManagedValuesManager(
    887     self.managed_values_dict, config, self
    888 ) as managed:
    890     def put_writes(task_id: str, writes: Sequence[tuple[str, Any]]) -> None:
    891         if self.checkpointer is not None:

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:144, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
    142 if typ is None:
    143     try:
--> 144         next(self.gen)
    145     except StopIteration:
    146         return False

File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/pregel/executor.py:70, in BackgroundExecutor(config)
     67         # executor waits for all tasks to finish on exit
     68 for task in tasks:
     69     # the first task to have raised an exception will be re-raised here
---> 70     task.result()

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:449, in Future.result(self, timeout)
    447     raise CancelledError()
    448 elif self._state == FINISHED:
--> 449     return self.__get_result()
    451 self._condition.wait(timeout)
    453 if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:401, in Future.__get_result(self)
    399 if self._exception:
    400     try:
--> 401         raise self._exception
    402     finally:
    403         # Break a reference cycle with the exception in self._exception
    404         self = None

File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/pregel/executor.py:43, in BackgroundExecutor.<locals>.done(task)
     41 def done(task: concurrent.futures.Future) -> None:
     42     try:
---> 43         task.result()
     44     except BaseException:
     45         pass

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:449, in Future.result(self, timeout)
    447     raise CancelledError()
    448 elif self._state == FINISHED:
--> 449     return self.__get_result()
    451 self._condition.wait(timeout)
    453 if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:401, in Future.__get_result(self)
    399 if self._exception:
    400     try:
--> 401         raise self._exception
    402     finally:
    403         # Break a reference cycle with the exception in self._exception
    404         self = None

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/thread.py:58, in _WorkItem.run(self)
     55     return
     57 try:
---> 58     result = self.fn(*self.args, **self.kwargs)
     59 except BaseException as exc:
     60     self.future.set_exception(exc)

File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/checkpoint/memory.py:168, in MemorySaver.put(self, config, checkpoint, metadata)
    147 def put(
    148     self,
    149     config: RunnableConfig,
    150     checkpoint: Checkpoint,
    151     metadata: CheckpointMetadata,
    152 ) -> RunnableConfig:
    153     """Save a checkpoint to the in-memory storage.
    154 
    155     This method saves a checkpoint to the in-memory storage. The checkpoint is associated
   (...)
    163         RunnableConfig: The updated config containing the saved checkpoint's timestamp.
    164     """
    165     self.storage[config["configurable"]["thread_id"]].update(
    166         {
    167             checkpoint["id"]: (
--> 168                 self.serde.dumps(checkpoint),
    169                 self.serde.dumps(metadata),
    170             )
    171         }
    172     )
    173     return {
    174         "configurable": {
    175             "thread_id": config["configurable"]["thread_id"],
    176             "thread_ts": checkpoint["id"],
    177         }
    178     }

File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/serde/jsonplus.py:98, in JsonPlusSerializer.dumps(self, obj)
     97 def dumps(self, obj: Any) -> bytes:
---> 98     return json.dumps(obj, default=self._default, ensure_ascii=False).encode(
     99         "utf-8", "ignore"
    100     )

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/__init__.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    232 if cls is None:
    233     cls = JSONEncoder
    234 return cls(
    235     skipkeys=skipkeys, ensure_ascii=ensure_ascii,
    236     check_circular=check_circular, allow_nan=allow_nan, indent=indent,
    237     separators=separators, default=default, sort_keys=sort_keys,
--> 238     **kw).encode(obj)

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:200, in JSONEncoder.encode(self, o)
    196         return encode_basestring(o)
    197 # This doesn't pass the iterator directly to ''.join() because the
    198 # exceptions aren't as detailed.  The list call should be roughly
    199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
    201 if not isinstance(chunks, (list, tuple)):
    202     chunks = list(chunks)

File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
    253 else:
    254     _iterencode = _make_iterencode(
    255         markers, self.default, _encoder, self.indent, floatstr,
    256         self.key_separator, self.item_separator, self.sort_keys,
    257         self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)

File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/serde/jsonplus.py:72, in JsonPlusSerializer._default(self, obj)
     68     return self._encode_constructor_args(
     69         obj.__class__, kwargs={"node": obj.node, "arg": obj.arg}
     70     )
     71 else:
---> 72     raise TypeError(
     73         f"Object of type {obj.__class__.__name__} is not JSON serializable"
     74     )

TypeError: Object of type Decimal is not JSON serializable

Description

When the tool message contains artifact, it failed to save to memory because it is raw and not stringified
error:
TypeError: Object of type Decimal is not JSON serializable

System Info

NA

Originally created by @HaoXuAI on GitHub (Jul 22, 2024). ### Checked other resources - [X] I added a very descriptive title to this issue. - [X] I searched the [LangGraph](https://langchain-ai.github.io/langgraph/)/LangChain documentation with the integrated search. - [X] I used the GitHub search to find a similar question and didn't find it. - [X] I am sure that this is a bug in LangGraph/LangChain rather than my code. - [X] I am sure this is better as an issue [rather than a GitHub discussion](https://github.com/langchain-ai/langgraph/discussions/new/choose), since this is a LangGraph bug and not a design question. ### Example Code ```python (content='success', name='sql_database_query', id='e419a154-04ef-4df5-b04a-e3e6961abb81', tool_call_id='call_bD8KUN0fUAVx4BmcONICNVnv', artifact=[('Customer#000143500', Decimal('7012696.48')), ('Customer#000095257', Decimal('6563511.23')), ('Customer#000087115', Decimal('6457526.26')), ('Customer#000131113', Decimal('6311428.86')), ('Customer#000103834', Decimal('6306524.23'))])`` ``` ### Error Message and Stack Trace (if applicable) ```shell --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[4], line 1 ----> 1 sql_agent.stream("I want to get the top 5 customers which making the most purchases", tool_mode=ToolMode.CONTINUOUS, display=True) File ~/dev/aita/aita/aita/agent/graph.py:171, in GraphAgent.stream(self, question, tool_mode, max_iterations, display) 169 if display: 170 print(f"Iteration {iterations}") --> 171 self._print_graph_events(events, _printed) 172 current_state = self.get_current_state() 173 if self.output_parser: File ~/dev/aita/aita/aita/agent/graph.py:257, in GraphAgent._print_graph_events(self, events, printed, max_length) 255 if isinstance(events, dict): 256 events = [events] --> 257 for event in events: 258 messages = self._get_event_message(event) 259 for message in messages: File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/pregel/__init__.py:884, in Pregel.stream(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug) 882 start = saved.metadata.get("step", -2) + 1 if saved else -1 883 # create channels from checkpoint --> 884 with BackgroundExecutor(config) as submit, ChannelsManager( 885 self.channels, checkpoint, config 886 ) as channels, ManagedValuesManager( 887 self.managed_values_dict, config, self 888 ) as managed: 890 def put_writes(task_id: str, writes: Sequence[tuple[str, Any]]) -> None: 891 if self.checkpointer is not None: File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:144, in _GeneratorContextManager.__exit__(self, typ, value, traceback) 142 if typ is None: 143 try: --> 144 next(self.gen) 145 except StopIteration: 146 return False File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/pregel/executor.py:70, in BackgroundExecutor(config) 67 # executor waits for all tasks to finish on exit 68 for task in tasks: 69 # the first task to have raised an exception will be re-raised here ---> 70 task.result() File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:449, in Future.result(self, timeout) 447 raise CancelledError() 448 elif self._state == FINISHED: --> 449 return self.__get_result() 451 self._condition.wait(timeout) 453 if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:401, in Future.__get_result(self) 399 if self._exception: 400 try: --> 401 raise self._exception 402 finally: 403 # Break a reference cycle with the exception in self._exception 404 self = None File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/pregel/executor.py:43, in BackgroundExecutor.<locals>.done(task) 41 def done(task: concurrent.futures.Future) -> None: 42 try: ---> 43 task.result() 44 except BaseException: 45 pass File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:449, in Future.result(self, timeout) 447 raise CancelledError() 448 elif self._state == FINISHED: --> 449 return self.__get_result() 451 self._condition.wait(timeout) 453 if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py:401, in Future.__get_result(self) 399 if self._exception: 400 try: --> 401 raise self._exception 402 finally: 403 # Break a reference cycle with the exception in self._exception 404 self = None File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/thread.py:58, in _WorkItem.run(self) 55 return 57 try: ---> 58 result = self.fn(*self.args, **self.kwargs) 59 except BaseException as exc: 60 self.future.set_exception(exc) File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/checkpoint/memory.py:168, in MemorySaver.put(self, config, checkpoint, metadata) 147 def put( 148 self, 149 config: RunnableConfig, 150 checkpoint: Checkpoint, 151 metadata: CheckpointMetadata, 152 ) -> RunnableConfig: 153 """Save a checkpoint to the in-memory storage. 154 155 This method saves a checkpoint to the in-memory storage. The checkpoint is associated (...) 163 RunnableConfig: The updated config containing the saved checkpoint's timestamp. 164 """ 165 self.storage[config["configurable"]["thread_id"]].update( 166 { 167 checkpoint["id"]: ( --> 168 self.serde.dumps(checkpoint), 169 self.serde.dumps(metadata), 170 ) 171 } 172 ) 173 return { 174 "configurable": { 175 "thread_id": config["configurable"]["thread_id"], 176 "thread_ts": checkpoint["id"], 177 } 178 } File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/serde/jsonplus.py:98, in JsonPlusSerializer.dumps(self, obj) 97 def dumps(self, obj: Any) -> bytes: ---> 98 return json.dumps(obj, default=self._default, ensure_ascii=False).encode( 99 "utf-8", "ignore" 100 ) File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/__init__.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw) 232 if cls is None: 233 cls = JSONEncoder 234 return cls( 235 skipkeys=skipkeys, ensure_ascii=ensure_ascii, 236 check_circular=check_circular, allow_nan=allow_nan, indent=indent, 237 separators=separators, default=default, sort_keys=sort_keys, --> 238 **kw).encode(obj) File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:200, in JSONEncoder.encode(self, o) 196 return encode_basestring(o) 197 # This doesn't pass the iterator directly to ''.join() because the 198 # exceptions aren't as detailed. The list call should be roughly 199 # equivalent to the PySequence_Fast that ''.join() would do. --> 200 chunks = self.iterencode(o, _one_shot=True) 201 if not isinstance(chunks, (list, tuple)): 202 chunks = list(chunks) File /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/json/encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot) 253 else: 254 _iterencode = _make_iterencode( 255 markers, self.default, _encoder, self.indent, floatstr, 256 self.key_separator, self.item_separator, self.sort_keys, 257 self.skipkeys, _one_shot) --> 258 return _iterencode(o, 0) File ~/Library/Caches/pypoetry/virtualenvs/aita-YD6hEonJ-py3.12/lib/python3.12/site-packages/langgraph/serde/jsonplus.py:72, in JsonPlusSerializer._default(self, obj) 68 return self._encode_constructor_args( 69 obj.__class__, kwargs={"node": obj.node, "arg": obj.arg} 70 ) 71 else: ---> 72 raise TypeError( 73 f"Object of type {obj.__class__.__name__} is not JSON serializable" 74 ) TypeError: Object of type Decimal is not JSON serializable ``` ### Description When the tool message contains artifact, it failed to save to memory because it is raw and not stringified error: `TypeError: Object of type Decimal is not JSON serializable` ### System Info NA
yindo closed this issue 2026-02-20 17:29:44 -05:00
Author
Owner

@hinthornw commented on GitHub (Jul 22, 2024):

Right now the default SerDe only handle a slightly expanded set of types beyond what is natively JSON serializable. Cc @vbarda to double check, but afaik, you can either:

  1. Easier: convert the artifact to an object that would be serializable (e.g., e.g., a python dataclass + cast decimals to floats)
  2. Harder: write your own serde implementation that uses something like pickling to manage things.
@hinthornw commented on GitHub (Jul 22, 2024): Right now the default SerDe only handle a slightly expanded set of types beyond what is natively JSON serializable. Cc @vbarda to double check, but afaik, you can either: 1. Easier: convert the artifact to an object that would be serializable (e.g., e.g., a python dataclass + cast decimals to floats) 2. Harder: write your own serde implementation that uses something like pickling to manage things.
Author
Owner

@vbarda commented on GitHub (Jul 22, 2024):

Right now the default SerDe only handle a slightly expanded set of types beyond what is natively JSON serializable. Cc @vbarda to double check, but afaik, you can either:

  1. Easier: convert the artifact to an object that would be serializable (e.g., e.g., a python dataclass + cast decimals to floats)
  2. Harder: write your own serde implementation that uses something like pickling to manage things.

yup, double checked and seeing the same error if the artifact is not serializable. +1 to the suggestions. if this happens for multiple tools that you use, it might make more sense to write your own serde logic. see the current implementation here https://github.com/langchain-ai/langgraph/blob/main/libs/langgraph/langgraph/serde/jsonplus.py#L18

@vbarda commented on GitHub (Jul 22, 2024): > Right now the default SerDe only handle a slightly expanded set of types beyond what is natively JSON serializable. Cc @vbarda to double check, but afaik, you can either: > > 1. Easier: convert the artifact to an object that would be serializable (e.g., e.g., a python dataclass + cast decimals to floats) > 2. Harder: write your own serde implementation that uses something like pickling to manage things. yup, double checked and seeing the same error if the artifact is not serializable. +1 to the suggestions. if this happens for multiple tools that you use, it might make more sense to write your own serde logic. see the current implementation here https://github.com/langchain-ai/langgraph/blob/main/libs/langgraph/langgraph/serde/jsonplus.py#L18
Author
Owner

@vbarda commented on GitHub (Jul 22, 2024):

@HaoXuAI going to close this, but feel free to reach out if you run into issues with either of the proposed solutions from above

@vbarda commented on GitHub (Jul 22, 2024): @HaoXuAI going to close this, but feel free to reach out if you run into issues with either of the proposed solutions from above
Author
Owner

@slittlec commented on GitHub (Jul 29, 2024):

I've also ran into this issue and there's quite a lot of types that run into this issue, including date, time, datetime.
When converting date and time to JSON and back, it gets quite messy, i.e. adding dates to the time and adding time to the dates.

I'm not too fussed about the artifact from the tool call being saved to memory.
Is there a way to prevent the artifact from being saved to memory?

If I'm understanding correctly, the artifact is a way to prevent a section of the tool call return being passed to the LLM. For example, maybe you have a large DataFrame you want in messages for future reference, but don't want to waste tokens sending that to the LLM.

Although I still can't figure out how to prevent this artifact being saved in the checkpointer memory and causing this issue specifically for a tool call which would be the cleanest implementation.

In other words, It'd be nice to have this artifact in local message history but not in the checkpointer memory which causes this issue, but still have the convo and other simple data types in the memory.

Any help would be appreciated.

@slittlec commented on GitHub (Jul 29, 2024): I've also ran into this issue and there's quite a lot of types that run into this issue, including date, time, datetime. When converting date and time to JSON and back, it gets quite messy, i.e. adding dates to the time and adding time to the dates. I'm not too fussed about the artifact from the tool call being saved to memory. **Is there a way to prevent the artifact from being saved to memory?** If I'm understanding correctly, the artifact is a way to prevent a section of the tool call return being passed to the LLM. For example, maybe you have a large DataFrame you want in messages for future reference, but don't want to waste tokens sending that to the LLM. Although I still can't figure out how to prevent this artifact being saved in the checkpointer memory and causing this issue **specifically for a tool call** which would be the cleanest implementation. In other words, It'd be nice to have this artifact in local message history but not in the checkpointer memory which causes this issue, but still have the convo and other simple data types in the memory. Any help would be appreciated.
Author
Owner

@vbarda commented on GitHub (Jul 30, 2024):

I've also ran into this issue and there's quite a lot of types that run into this issue, including date, time, datetime. When converting date and time to JSON and back, it gets quite messy, i.e. adding dates to the time and adding time to the dates.

I'm not too fussed about the artifact from the tool call being saved to memory. Is there a way to prevent the artifact from being saved to memory?

If I'm understanding correctly, the artifact is a way to prevent a section of the tool call return being passed to the LLM. For example, maybe you have a large DataFrame you want in messages for future reference, but don't want to waste tokens sending that to the LLM.

Although I still can't figure out how to prevent this artifact being saved in the checkpointer memory and causing this issue specifically for a tool call which would be the cleanest implementation.

In other words, It'd be nice to have this artifact in local message history but not in the checkpointer memory which causes this issue, but still have the convo and other simple data types in the memory.

Any help would be appreciated.

@slittlec we don't have a mechanism for preventing artifact from being saved in checkpointer (and don't think we will). are you using a custom tool or some prebuilt tool from langchain libraries? if having artifact in messages is a hard requirement, you would need to still implement custom serialization/deserialization logic. otherwise, perhaps you can just disable the artifact altogether?

@vbarda commented on GitHub (Jul 30, 2024): > I've also ran into this issue and there's quite a lot of types that run into this issue, including date, time, datetime. When converting date and time to JSON and back, it gets quite messy, i.e. adding dates to the time and adding time to the dates. > > I'm not too fussed about the artifact from the tool call being saved to memory. **Is there a way to prevent the artifact from being saved to memory?** > > If I'm understanding correctly, the artifact is a way to prevent a section of the tool call return being passed to the LLM. For example, maybe you have a large DataFrame you want in messages for future reference, but don't want to waste tokens sending that to the LLM. > > Although I still can't figure out how to prevent this artifact being saved in the checkpointer memory and causing this issue **specifically for a tool call** which would be the cleanest implementation. > > In other words, It'd be nice to have this artifact in local message history but not in the checkpointer memory which causes this issue, but still have the convo and other simple data types in the memory. > > Any help would be appreciated. @slittlec we don't have a mechanism for preventing artifact from being saved in checkpointer (and don't think we will). are you using a custom tool or some prebuilt tool from langchain libraries? if having artifact in messages is a hard requirement, you would need to still implement custom serialization/deserialization logic. otherwise, perhaps you can just disable the artifact altogether?
Author
Owner

@slittlec commented on GitHub (Jul 30, 2024):

I've also ran into this issue and there's quite a lot of types that run into this issue, including date, time, datetime. When converting date and time to JSON and back, it gets quite messy, i.e. adding dates to the time and adding time to the dates.
I'm not too fussed about the artifact from the tool call being saved to memory. Is there a way to prevent the artifact from being saved to memory?
If I'm understanding correctly, the artifact is a way to prevent a section of the tool call return being passed to the LLM. For example, maybe you have a large DataFrame you want in messages for future reference, but don't want to waste tokens sending that to the LLM.
Although I still can't figure out how to prevent this artifact being saved in the checkpointer memory and causing this issue specifically for a tool call which would be the cleanest implementation.
In other words, It'd be nice to have this artifact in local message history but not in the checkpointer memory which causes this issue, but still have the convo and other simple data types in the memory.
Any help would be appreciated.

@slittlec we don't have a mechanism for preventing artifact from being saved in checkpointer (and don't think we will). are you using a custom tool or some prebuilt tool from langchain libraries? if having artifact in messages is a hard requirement, you would need to still implement custom serialization/deserialization logic. otherwise, perhaps you can just disable the artifact altogether?

Thanks good to know @vbarda, what I'm trying to achieve is passing around a large dataframe with dynamic types or without having to store it as a global object. This would also prevent a lot of type conversions and make it more efficient for large datasets. I was using a custom tool.

I thought another way would be, maybe I could use a context object (empty df) in state to prevent it being stored in checkpointer, then pass state into tool but then I'm not sure how to update state (full df) in the custom tool. I've detailed a larger response below.
https://github.com/langchain-ai/langgraph/issues/54#issuecomment-2257353060

@slittlec commented on GitHub (Jul 30, 2024): > > I've also ran into this issue and there's quite a lot of types that run into this issue, including date, time, datetime. When converting date and time to JSON and back, it gets quite messy, i.e. adding dates to the time and adding time to the dates. > > I'm not too fussed about the artifact from the tool call being saved to memory. **Is there a way to prevent the artifact from being saved to memory?** > > If I'm understanding correctly, the artifact is a way to prevent a section of the tool call return being passed to the LLM. For example, maybe you have a large DataFrame you want in messages for future reference, but don't want to waste tokens sending that to the LLM. > > Although I still can't figure out how to prevent this artifact being saved in the checkpointer memory and causing this issue **specifically for a tool call** which would be the cleanest implementation. > > In other words, It'd be nice to have this artifact in local message history but not in the checkpointer memory which causes this issue, but still have the convo and other simple data types in the memory. > > Any help would be appreciated. > > @slittlec we don't have a mechanism for preventing artifact from being saved in checkpointer (and don't think we will). are you using a custom tool or some prebuilt tool from langchain libraries? if having artifact in messages is a hard requirement, you would need to still implement custom serialization/deserialization logic. otherwise, perhaps you can just disable the artifact altogether? Thanks good to know @vbarda, what I'm trying to achieve is passing around a large dataframe with dynamic types or without having to store it as a global object. This would also prevent a lot of type conversions and make it more efficient for large datasets. I was using a custom tool. I thought another way would be, maybe I could use a context object (empty df) in state to prevent it being stored in checkpointer, then pass state into tool but then I'm not sure how to update state (full df) in the custom tool. I've detailed a larger response below. https://github.com/langchain-ai/langgraph/issues/54#issuecomment-2257353060
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#156