Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix JSON serialization error in Ollama models #1129

Merged
merged 4 commits into from
Dec 5, 2024
Merged

Conversation

JanusChoi
Copy link
Contributor

A slightly changes in order to fix: #1128

issue: When chat with Jupyternaut, getting error:

[D 2024-11-30 07:52:05.120 ServerApp] Broadcasting message: type='agent-stream-chunk' id='0a3f8b24522a46c9a694a8e2493e561b' content='' stream_complete=True metadata={'model': 'deepseek-v2:16b', 'created_at': '2024-11-29T23:52:04.8986414Z', 'done': True, 'done_reason': 'stop', 'total_duration': 4248175500, 'load_duration': 3461159000, 'prompt_eval_count': 265, 'prompt_eval_duration': 345000000, 'eval_count': 20, 'eval_duration': 322000000, 'message': Message(role='assistant', content='', images=None, tool_calls=None)} to all clients...
[E 2024-11-30 07:52:05.120 AiExtension] Object of type Message is not JSON serializable
[D 2024-11-30 07:52:05.204 ServerApp] Broadcasting message: id='0b38a635d3a34f0da5b5bb23e0a789d6' time=1732924325.204742 body='Sorry, an error occurred. Details below:\n\n```\nTraceback (most recent call last):\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\jupyter_ai\\chat_handlers\\base.py", line 226, in on_message\n    await self.process_message(message)\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\jupyter_ai\\chat_handlers\\default.py", line 71, in process_message\n    await self.stream_reply(inputs, message)\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\jupyter_ai\\chat_handlers\\base.py", line 603, in stream_reply\n    self._send_stream_chunk(\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\jupyter_ai\\chat_handlers\\base.py", line 518, in _send_stream_chunk\n    self.broadcast_message(stream_chunk_msg)\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\jupyter_ai\\chat_handlers\\base.py", line 285, in broadcast_message\n    websocket.broadcast_message(message)\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\jupyter_ai\\handlers.py", line 241, in broadcast_message\n    client.write_message(message.dict())\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\tornado\\websocket.py", line 334, in write_message\n    message = tornado.escape.json_encode(message)\n              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\site-packages\\tornado\\escape.py", line 96, in json_encode\n    return json.dumps(value).replace("</", "<\\\\/")\n           ^^^^^^^^^^^^^^^^^\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\json\\__init__.py", line 231, in dumps\n    return _default_encoder.encode(obj)\n           
^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\json\\encoder.py", line 200, in encode\n    chunks = self.iterencode(o, _one_shot=True)\n           
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\json\\encoder.py", line 258, in iterencode\n    return _iterencode(o, 0)\n           ^^^^^^^^^^^^^^^^^\n  File "D:\\miniconda3\\envs\\jupyter-ai\\Lib\\json\\encoder.py", line 180, in default\n    raise TypeError(f\'Object of type {o.__class__.__name__} \'\nTypeError: Object of type Message is not JSON serializable\n\n```' reply_to='8bc8ec45-6453-40e3-8dc1-f3e1f1f40227' persona=Persona(name='Jupyternaut', avatar_route='api/ai/static/jupyternaut.svg') metadata={} type='agent' to all clients...
[I 2024-11-30 07:52:05.206 ServerApp] Default chat handler resolved in 4754 ms.

The error occurs when trying to serialize a Message object to JSON. Looking at the code, the Message class is defined as a Union type of several message classes:

Message = Union[
   ChatMessage,
   ConnectionMessage,
   ClearMessage,
   PendingMessage,
   ClosePendingMessage,
]

The error occurs because the metadata field in the message contains a Message object, which is not directly JSON serializable.

The issue is that the metadata field is defined as Dict[str, Any], which means it can contain any type of value. In this case, it appears to contain a Message object which can't be automatically serialized to JSON.

Edit the AgentStreamChunkMessage class to ensure metadata values are JSON serializable:

site-packages\jupyter_ai\models.py

import json

class AgentStreamChunkMessage(BaseModel):
    ## {...}

    @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)}")

The metadata is being populated from the LLMResult's generation_info dictionary, which may contain objects that are not JSON serializable. We need to ensure that any non-serializable objects in the metadata are converted to a serializable format before being assigned.

So modify the MetadataCallbackHandler to handle this:

site-packages\jupyter_ai\callback_handlers\metadata.py

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):
    """
    When passed as a callback handler, this stores the LLMResult's
    `generation_info` dictionary in the `self.jai_metadata` instance attribute
    after the provider fully processes an input.

    If used in a streaming chat handler: the `metadata` field of the final
    `AgentStreamChunkMessage` should be set to `self.jai_metadata`.

    If used in a non-streaming chat handler: the `metadata` field of the
    returned `AgentChatMessage` should be set to `self.jai_metadata`.
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.jai_metadata = {}

    def on_llm_end(self, response: LLMResult, **kwargs) -> None:
        if not (len(response.generations) and len(response.generations[0])):
            return

        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

@krassowski krassowski added the bug Something isn't working label Nov 30, 2024
@krassowski krassowski changed the title fixing https://github.com/jupyterlab/jupyter-ai/issues/1128 Fix JSON serialization error in Ollama models Nov 30, 2024
@krassowski
Copy link
Member

Indeed, it looks like any arbitrary value may be present in generation_data (so it is not an issue with Ollama!) as per definition of this field in langchain:

    generation_info: Optional[dict[str, Any]] = None
    """Raw response from the provider.

    May include things like the reason for finishing or token log probabilities.
    """

Copy link
Member

@dlqqq dlqqq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JanusChoi Thank you for contributing this fix so quickly! Looks like the fix works, per Sanjiv's testing. Awesome work. 🎉

Can you help modify this PR according to my review below? I've included a summary of @krassowski's review in mine. One of our contributors can help if you lack the time. 👍

@dlqqq
Copy link
Member

dlqqq commented Dec 4, 2024

I can help with these changes proposed above so we can include this in the next patch release. Working on this now.

Copy link
Collaborator

@srdas srdas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested this with chat and RAG to make sure there is no error.

Also the function to convert to serializable works, as can be seen
image

All else looks good. Thanks so much @dlqqq (and @JanusChoi @krassowski) for this.

@dlqqq dlqqq merged commit 3a8016a into jupyterlab:main Dec 5, 2024
10 checks passed
@dlqqq
Copy link
Member

dlqqq commented Dec 5, 2024

@meeseeksdev please backport to v3-dev

meeseeksmachine pushed a commit to meeseeksmachine/jupyter-ai that referenced this pull request Dec 5, 2024
dlqqq pushed a commit that referenced this pull request Dec 5, 2024
dlqqq added a commit that referenced this pull request Dec 26, 2024
* Backport PR #1049: Added new Anthropic Sonnet3.5 v2 models (#1050)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1051: Added Developer documentation for streaming responses (#1058)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1048: Implement streaming for `/fix` (#1059)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1057: [pre-commit.ci] pre-commit autoupdate (#1060)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Backport PR #1064: Added Ollama to the providers table in user docs (#1066)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1056: Add examples of using Fields and EnvAuthStrategy to developer documentation (#1073)

Co-authored-by: Alan Meeson <[email protected]>

* Backport PR #1069: Merge Anthropic language model providers (#1076)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1068: Allow `$` to literally denote quantities of USD in chat (#1079)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1075: Fix magic commands when using non-chat providers w/ history (#1080)

Co-authored-by: Alan Meeson <[email protected]>

* Backport PR #1077: Fix `/export` by including streamed agent messages (#1081)

Co-authored-by: Mahmut CAVDAR <[email protected]>

* Backport PR #1072: Reduced padding in cell around code icons in code toolbar (#1084)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1087: Improve installation documentation and clarify provider dependencies (#1091)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1092: Remove retired models and add new `Haiku-3.5` model in Anthropic (#1093)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1094: Continue to allow `$` symbols to delimit inline math in human messages (#1095)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1097: Update `faiss-cpu` version range (#1101)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1104: Fix rendering of code blocks in JupyterLab 4.3.0+ (#1105)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1106: Catch error on non plaintext files in `@file` and reply gracefully in chat (#1110)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1109: Bump LangChain minimum versions (#1112)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1119: Downgrade spurious 'error' logs (#1124)

Co-authored-by: ctcjab <[email protected]>

* Backport PR #1127: Removes outdated OpenAI models and adds new ones (#1130)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1131: [pre-commit.ci] pre-commit autoupdate (#1132)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Backport PR #1125: Update model fields immediately on save (#1133)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1139: Fix install step in CI (#1140)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1129: Fix JSON serialization error in Ollama models (#1141)

Co-authored-by: Mr.W <[email protected]>

* Backport PR #1137: Update completion model fields immediately on save (#1142)

Co-authored-by: david qiu <[email protected]>

* [v3-dev] Initial migration to `jupyterlab-chat` (#1043)

* Very first version of the AI working in jupyterlab_collaborative_chat

* Allows both collaborative and regular chat to work with AI

* handle the help message in the chat too

* Autocompletion (#2)

* Fix handler methods' parameters

* Add slash commands (autocompletion) to the chat input

* Stream messages (#3)

* Allow for stream messages

* update jupyter collaborative chat dependency

* AI settings (#4)

* Add a menu option to open the AI settings

* Remove the input option from the setting widget

* pre-commit

* linting

* Homogeneize typing for optional arguments

* Fix import

* Showing that the bot is writing (answering) (#5)

* Show that the bot is writing (answering)

* Update jupyter chat dependency

* Some typing

* Update extension to jupyterlab_chat (0.6.0) (#8)

* Fix linting

* Remove try/except to import jupyterlab_chat (not optional anymore), and fix typing

* linter

* Python unit tests

* Fix typing

* lint

* Fix lint and mypy all together

* Fix web_app settings accessor

* Fix jupyter_collaboration version

Co-authored-by: david qiu <[email protected]>

* Remove unecessary try/except

* Dedicate one set of chat handlers per room (#9)

* create new set of chat handlers per room

* make YChat an instance attribute on BaseChatHandler

* revert changes to chat handlers

* pre-commit

* use room_id local var

Co-authored-by: Nicolas Brichet <[email protected]>

---------

Co-authored-by: Nicolas Brichet <[email protected]>

---------

Co-authored-by: david qiu <[email protected]>
Co-authored-by: david qiu <[email protected]>

* Backport PR #1134: Improve user messaging and documentation for Cross-Region Inference on Amazon Bedrock (#1143)

Co-authored-by: Sanjiv Das <[email protected]>

* Backport PR #1136: Add base API URL field for Ollama and OpenAI embedding models (#1149)

Co-authored-by: Sanjiv Das <[email protected]>

* [v3-dev] Remove `/export`, `/clear`, and `/fix` (#1148)

* remove /export

* remove /clear

* remove /fix

* Fix CI in `v3-dev` branch (#1154)

* fix check release by bumping to impossible version

* fix types

* Update Playwright Snapshots

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [v3-dev] Dedicate one LangChain history object per chat (#1151)

* dedicate a separate LangChain history object per chat

* pre-commit

* fix mypy

* Backport PR #1160: Trigger update snapshots based on commenter's role (#1161)

Co-authored-by: david qiu <[email protected]>

* Backport PR #1155: Fix code output format in IPython (#1162)

Co-authored-by: Divyansh Choudhary <[email protected]>

* Backport PR #1158: Update `/generate` to not split classes & functions across cells (#1164)

Co-authored-by: Sanjiv Das <[email protected]>

* Remove v2 frontend components (#1156)

* First pass to remove the front end chat

* Remove code-toolbar by using a simplified markdown renderer in settings

* Remove chat-message-menu (should be ported in jupyter-chat)

* Remove chat handler

* Follow up 'Remove chat-message-menu (should be ported in jupyter-chat)' commit

* Clean package.json

* Remove UI tests

* Remove the generative AI menu

* Remove unused components

* run yarn dedupe

---------

Co-authored-by: David L. Qiu <[email protected]>

* Upgrade to `jupyterlab-chat>=0.7.0` (#1166)

* upgrade to jupyterlab-chat 0.7.0

* pre-commit

* upgrade to @jupyter/chat ^0.7.0 in frontend

* Remove v2 backend components (#1168)

* remove v2 llm memory, implement ReplyStream

* remove v2 websockets & REST handlers

* remove unused v2 data models

* fix slash command autocomplete

* fix unit tests

* remove unused _learned context provider

* fix mypy

* pre-commit

* fix optional k arg in YChatHistory

* bump jupyter chat to 0.7.1 to fix Python 3.9 tests

* revert accidentally breaking /learn

---------

Co-authored-by: Lumberbot (aka Jack) <[email protected]>
Co-authored-by: Sanjiv Das <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alan Meeson <[email protected]>
Co-authored-by: Mahmut CAVDAR <[email protected]>
Co-authored-by: ctcjab <[email protected]>
Co-authored-by: Mr.W <[email protected]>
Co-authored-by: Nicolas Brichet <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Divyansh Choudhary <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ollama models return JSON serializing error
4 participants