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

_pending_writes should be non-None when ChatInterface.send with callback is called before served #6521

Closed
ahuang11 opened this issue Mar 18, 2024 · 4 comments · Fixed by #6528
Labels
type: bug Something isn't correct or isn't working
Milestone

Comments

@ahuang11
Copy link
Contributor

ahuang11 commented Mar 18, 2024

import panel as pn

def callback(contents, user, interface):
    for i in range(0, 12):
        message = chat_feed.stream(f"new message {i}", message=message)

chat_feed = pn.chat.ChatInterface(callback=callback)
message = pn.chat.ChatMessage(user="System")
chat_feed.send(message)
chat_feed.servable()
Traceback (most recent call last):
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/tornado/ioloop.py", line 738, in _run_callback
    ret = callback()
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/tornado/ioloop.py", line 762, in _discard_future_result
    future.result()
  File "/Users/ahuang/repos/panel/panel/io/server.py", line 177, in wrapper
    state._handle_exception(e)
  File "/Users/ahuang/repos/panel/panel/io/state.py", line 445, in _handle_exception
    raise exception
  File "/Users/ahuang/repos/panel/panel/io/server.py", line 175, in wrapper
    return await func(*args, **kw)
  File "/Users/ahuang/repos/panel/panel/chat/feed.py", line 537, in _prepare_response
    await self._cleanup_response()
  File "/Users/ahuang/repos/panel/panel/chat/interface.py", line 605, in _cleanup_response
    await super()._cleanup_response()
  File "/Users/ahuang/repos/panel/panel/chat/feed.py", line 543, in _cleanup_response
    with param.parameterized.batch_call_watchers(self):
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/contextlib.py", line 142, in __exit__
    next(self.gen)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 307, in batch_call_watchers
    parameterized.param._batch_call_watchers()
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 2506, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 2468, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/Users/ahuang/repos/panel/panel/reactive.py", line 675, in link_cb
    setattr(target, links[event.name], event.new)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 525, in _f
    instance_param.__set__(obj, val)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 527, in _f
    return f(self, obj, val)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 1545, in __set__
    obj.param._call_watcher(watcher, event)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 2486, in _call_watcher
    self_._execute_watcher(watcher, (event,))
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/param/parameterized.py", line 2468, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/Users/ahuang/repos/panel/panel/reactive.py", line 373, in _param_change
    self._apply_update(named_events, properties, model, ref)
  File "/Users/ahuang/repos/panel/panel/reactive.py", line 301, in _apply_update
    self._update_model(events, msg, root, model, doc, comm)
  File "/Users/ahuang/repos/panel/panel/reactive.py", line 622, in _update_model
    super()._update_model(events, msg, root, model, doc, comm)
  File "/Users/ahuang/repos/panel/panel/reactive.py", line 329, in _update_model
    model.update(**msg)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/core/has_props.py", line 485, in update
    setattr(self, k, v)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/core/has_props.py", line 338, in __setattr__
    return super().__setattr__(name, value)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/core/property/descriptors.py", line 333, in __set__
    self._set(obj, old, value, setter=setter)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/core/property/descriptors.py", line 621, in _set
    self._trigger(obj, old, value, hint=hint, setter=setter)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/core/property/descriptors.py", line 699, in _trigger
    obj.trigger(self.name, old, value, hint, setter)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/model/model.py", line 571, in trigger
    super().trigger(descriptor.name, old, new, hint=hint, setter=setter)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/util/callback_manager.py", line 188, in trigger
    self.document.callbacks.notify_change(cast(Model, self), attr, old, new, hint, setter, invoke)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/callbacks.py", line 249, in notify_change
    self.trigger_on_change(event)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/callbacks.py", line 413, in trigger_on_change
    invoke_with_curdoc(doc, invoke_callbacks)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/callbacks.py", line 443, in invoke_with_curdoc
    return f()
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/callbacks.py", line 412, in invoke_callbacks
    cb(event)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/callbacks.py", line 276, in <lambda>
    self._change_callbacks[receiver] = lambda event: event.dispatch(receiver)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/events.py", line 353, in dispatch
    super().dispatch(receiver)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/document/events.py", line 219, in dispatch
    cast(DocumentPatchedMixin, receiver)._document_patched(self)
  File "/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/server/session.py", line 244, in _document_patched
    raise RuntimeError("_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes")
RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes
@ahuang11 ahuang11 added the type: bug Something isn't correct or isn't working label Mar 18, 2024
@ahuang11 ahuang11 changed the title _pending_writes should be non-None when send is called before served. _pending_writes should be non-None when ChatInterface.send with callback is called before served Mar 18, 2024
@MarcSkovMadsen
Copy link
Collaborator

I've also seen these ones when working on the Major-Tom app.

@MarcSkovMadsen MarcSkovMadsen added this to the v1.4.0 milestone Mar 18, 2024
@philippjfr
Copy link
Member

ChatInterface really should not be running callbacks before the app is fully loaded.

@ahuang11
Copy link
Contributor Author

ahuang11 commented Mar 19, 2024

Seems reasonable though since isn't it the equivalent of:

import panel as pn

pn.extension()


def callback(event):
    print("Received message:", event.new)

button = pn.widgets.Button(name='Click me')
button.on_click(callback)
button.param.trigger("clicks") # similar to .send()
button.servable()

Which outputs:

2024-03-19 09:29:25,150 Starting Bokeh server version 3.4.0 (running on Tornado 6.3.3)
2024-03-19 09:29:25,151 User authentication hooks NOT provided (default user enabled)
2024-03-19 09:29:25,153 Bokeh app running at: http://localhost:5006/test
2024-03-19 09:29:25,153 Starting Bokeh server with process id: 79613
Received message: 0
2024-03-19 09:29:26,583 WebSocket connection opened
2024-03-19 09:29:26,583 ServerConnection created

Do you suggest there should be a queue of callbacks until onload, or explicitly raise ValueError("Callbacks should not run before the app is fully loaded")?

@philippjfr
Copy link
Member

philippjfr commented Mar 19, 2024

It's actually okay, but no, those are not the same. A synchronous function will execute immediately and block, meaning that when the application is initialized the callback (and any side-effects it applies) will already have run. With an async function on the other hand you don't know when the callback runs and in the worst case will execute during or shortly after serialization but before the websocket is loaded at which point you will get _pending_writes errors because the websocket is locked.

In the end though that's fine, I just need to make sure that the events aren't dispatched until the app is fully loaded and the WebSocket is open. There is still a clear failure case though, specifically writing an async callback that runs and modifies some Bokeh model directly will still run into the _pending_writes issue and there is effectively nothing we can do about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't correct or isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants