From 701aef0bea1272883a84c1e49c83c4246e256f59 Mon Sep 17 00:00:00 2001 From: JanusChoi Date: Sat, 30 Nov 2024 18:39:14 +0800 Subject: [PATCH 1/4] fixing https://github.com/jupyterlab/jupyter-ai/issues/1128 --- .../jupyter_ai/callback_handlers/metadata.py | 23 ++++++++++++++++++- packages/jupyter-ai/jupyter_ai/models.py | 10 ++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py index 145cab313..811155113 100644 --- a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py +++ b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py @@ -1,5 +1,15 @@ from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +import json + + +def convert_to_serializable(obj): + """Convert an object to a JSON serializable format""" + if hasattr(obj, 'dict') and callable(obj.dict): + return obj.dict() + if hasattr(obj, '__dict__'): + return obj.__dict__ + return str(obj) class MetadataCallbackHandler(BaseCallbackHandler): @@ -23,4 +33,15 @@ def on_llm_end(self, response: LLMResult, **kwargs) -> None: if not (len(response.generations) and len(response.generations[0])): return - self.jai_metadata = response.generations[0][0].generation_info or {} + metadata = response.generations[0][0].generation_info or {} + + # Convert any non-serializable objects in metadata + serializable_metadata = {} + for key, value in metadata.items(): + try: + json.dumps(value) + serializable_metadata[key] = value + except (TypeError, ValueError): + serializable_metadata[key] = convert_to_serializable(value) + + self.jai_metadata = serializable_metadata diff --git a/packages/jupyter-ai/jupyter_ai/models.py b/packages/jupyter-ai/jupyter_ai/models.py index 48dbe6193..4cb071ed9 100644 --- a/packages/jupyter-ai/jupyter_ai/models.py +++ b/packages/jupyter-ai/jupyter_ai/models.py @@ -1,4 +1,5 @@ from typing import Any, Dict, List, Literal, Optional, Union +import json from jupyter_ai_magics import Persona from jupyter_ai_magics.providers import AuthStrategy, Field @@ -127,6 +128,15 @@ class AgentStreamChunkMessage(BaseModel): chunk should override any metadata from previous chunks. See the docstring on `BaseAgentMessage.metadata` for information. """ + + @validator("metadata") + def validate_metadata(cls, v): + """Ensure metadata values are JSON serializable""" + try: + json.dumps(v) + return v + except TypeError as e: + raise ValueError(f"Metadata must be JSON serializable: {str(e)}") class HumanChatMessage(BaseModel): From 6970cbadcd97b10c5ac7b59ab22d62c9beee25b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 30 Nov 2024 10:58:26 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../jupyter_ai/callback_handlers/metadata.py | 11 ++++++----- packages/jupyter-ai/jupyter_ai/models.py | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py index 811155113..a618b2f26 100644 --- a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py +++ b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py @@ -1,13 +1,14 @@ +import json + from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult -import json def convert_to_serializable(obj): """Convert an object to a JSON serializable format""" - if hasattr(obj, 'dict') and callable(obj.dict): + if hasattr(obj, "dict") and callable(obj.dict): return obj.dict() - if hasattr(obj, '__dict__'): + if hasattr(obj, "__dict__"): return obj.__dict__ return str(obj) @@ -34,7 +35,7 @@ def on_llm_end(self, response: LLMResult, **kwargs) -> None: return metadata = response.generations[0][0].generation_info or {} - + # Convert any non-serializable objects in metadata serializable_metadata = {} for key, value in metadata.items(): @@ -43,5 +44,5 @@ def on_llm_end(self, response: LLMResult, **kwargs) -> None: serializable_metadata[key] = value except (TypeError, ValueError): serializable_metadata[key] = convert_to_serializable(value) - + self.jai_metadata = serializable_metadata diff --git a/packages/jupyter-ai/jupyter_ai/models.py b/packages/jupyter-ai/jupyter_ai/models.py index 4cb071ed9..6bd7d4e06 100644 --- a/packages/jupyter-ai/jupyter_ai/models.py +++ b/packages/jupyter-ai/jupyter_ai/models.py @@ -1,5 +1,5 @@ -from typing import Any, Dict, List, Literal, Optional, Union import json +from typing import Any, Dict, List, Literal, Optional, Union from jupyter_ai_magics import Persona from jupyter_ai_magics.providers import AuthStrategy, Field @@ -128,7 +128,7 @@ class AgentStreamChunkMessage(BaseModel): chunk should override any metadata from previous chunks. See the docstring on `BaseAgentMessage.metadata` for information. """ - + @validator("metadata") def validate_metadata(cls, v): """Ensure metadata values are JSON serializable""" From 99301a86e6ef627a726cf3c2f208827534624332 Mon Sep 17 00:00:00 2001 From: "David L. Qiu" Date: Wed, 4 Dec 2024 12:15:15 -0800 Subject: [PATCH 3/4] simplify impl and verify dict() requires no args --- .../jupyter_ai/callback_handlers/metadata.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py index a618b2f26..783af6362 100644 --- a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py +++ b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py @@ -1,12 +1,24 @@ import json +import inspect from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +def requires_no_arguments(func): + sig = inspect.signature(func) + for param in sig.parameters.values(): + if param.default is param.empty and param.kind in ( + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + param.KEYWORD_ONLY + ): + return False + return True + def convert_to_serializable(obj): """Convert an object to a JSON serializable format""" - if hasattr(obj, "dict") and callable(obj.dict): + if hasattr(obj, "dict") and callable(obj.dict) and requires_no_arguments(obj.dict): return obj.dict() if hasattr(obj, "__dict__"): return obj.__dict__ @@ -37,12 +49,9 @@ def on_llm_end(self, response: LLMResult, **kwargs) -> None: metadata = response.generations[0][0].generation_info or {} # Convert any non-serializable objects in metadata - serializable_metadata = {} - for key, value in metadata.items(): - try: - json.dumps(value) - serializable_metadata[key] = value - except (TypeError, ValueError): - serializable_metadata[key] = convert_to_serializable(value) - - self.jai_metadata = serializable_metadata + self.jai_metadata = json.loads( + json.dumps( + metadata, + default=convert_to_serializable + ) + ) From 4218196dfedacc241071be6c6885632e0f3802f0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:17:24 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../jupyter_ai/callback_handlers/metadata.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py index 783af6362..c409a9633 100644 --- a/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py +++ b/packages/jupyter-ai/jupyter_ai/callback_handlers/metadata.py @@ -1,16 +1,17 @@ -import json import inspect +import json from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult + def requires_no_arguments(func): sig = inspect.signature(func) for param in sig.parameters.values(): if param.default is param.empty and param.kind in ( - param.POSITIONAL_ONLY, - param.POSITIONAL_OR_KEYWORD, - param.KEYWORD_ONLY + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + param.KEYWORD_ONLY, ): return False return True @@ -50,8 +51,5 @@ def on_llm_end(self, response: LLMResult, **kwargs) -> None: # Convert any non-serializable objects in metadata self.jai_metadata = json.loads( - json.dumps( - metadata, - default=convert_to_serializable - ) + json.dumps(metadata, default=convert_to_serializable) )