Skip to content

pywry.chat

ACP-native chat system for PyWry — models, session primitives, update types, artifacts, providers, and the ChatManager orchestrator.


Manager

pywry.chat.manager.ChatManager

ChatManager(provider: Any = None, handler: HandlerFunc | None = None, *, system_prompt: str = '', model: str = '', temperature: float = 0.7, welcome_message: str = '', settings: Sequence[SettingsItem] | None = None, slash_commands: Sequence[Any] | None = None, on_slash_command: Callable[..., Any] | None = None, on_settings_change: Callable[..., Any] | None = None, show_sidebar: bool = True, show_settings: bool = True, toolbar_width: str = '380px', toolbar_min_width: str = '280px', collapsible: bool = True, resizable: bool = True, include_plotly: bool = False, include_aggrid: bool = False, aggrid_theme: str = 'alpine', enable_context: bool = False, enable_file_attach: bool = False, file_accept_types: list[str] | None = None, context_allowed_roots: list[str] | None = None)

ACP-native orchestrator for the PyWry chat component.

Handles event wiring, thread management, streaming, cancellation, and state synchronization. Accepts either a ChatProvider instance (ACP session lifecycle) or a handler function.

Parameters:

Name Type Description Default
provider ChatProvider | None

ACP-conformant provider instance.

None
handler callable | None

Handler function (messages, ctx) -> str | Iterator. Exactly one of provider or handler must be supplied.

None
system_prompt str

System prompt prepended to every request.

''
model str

Model identifier passed to handler via context.

''
temperature float

Temperature passed to handler via context.

0.7
welcome_message str

Markdown message sent when the chat initializes.

''
settings list[SettingsItem] | None

Settings items for the gear dropdown.

None
on_settings_change callable | None

Callback (key, value) when a setting changes.

None
show_sidebar bool

Show the conversation picker.

True
show_settings bool

Show the gear icon.

True
toolbar_width str

CSS width for the chat toolbar.

'380px'
toolbar_min_width str

CSS min-width for the chat toolbar.

'280px'
collapsible bool

Whether the toolbar is collapsible.

True
resizable bool

Whether the toolbar is resizable.

True
include_plotly bool

Include Plotly.js eagerly.

False
include_aggrid bool

Include AG Grid eagerly.

False
aggrid_theme str

AG Grid theme name.

'alpine'
enable_context bool

Enable @-mention widget references.

False
enable_file_attach bool

Enable file attachment button.

False
file_accept_types list[str] | None

Allowed file extensions (required when enable_file_attach=True).

None
context_allowed_roots list[str] | None

Restrict file attachments to these directories.

None

Attributes

CONTEXT_TOOL class-attribute

CONTEXT_TOOL: dict[str, Any] = {'type': 'function', 'function': {'name': 'get_context', 'description': 'Retrieve the full content of an attached file or widget. Call this when you need to read, analyze, or reference an attachment the user has provided.', 'parameters': {'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The attachment name.'}}, 'required': ['name']}}}

_provider instance-attribute

_provider = provider

_handler instance-attribute

_handler = handler

_system_prompt instance-attribute

_system_prompt = system_prompt

_model instance-attribute

_model = model

_temperature instance-attribute

_temperature = temperature

_welcome_message instance-attribute

_welcome_message = welcome_message

_settings_items instance-attribute

_settings_items = list(settings) if settings else []

_slash_commands instance-attribute

_slash_commands = list(slash_commands) if slash_commands else []

_on_slash_command instance-attribute

_on_slash_command = on_slash_command

_on_settings_change instance-attribute

_on_settings_change = on_settings_change

_show_sidebar instance-attribute

_show_sidebar = show_sidebar

_show_settings instance-attribute

_show_settings = show_settings

_toolbar_width instance-attribute

_toolbar_width = toolbar_width

_toolbar_min_width instance-attribute

_toolbar_min_width = toolbar_min_width

_collapsible instance-attribute

_collapsible = collapsible

_resizable instance-attribute

_resizable = resizable

_include_plotly instance-attribute

_include_plotly = include_plotly

_include_aggrid instance-attribute

_include_aggrid = include_aggrid

_aggrid_theme instance-attribute

_aggrid_theme = aggrid_theme

_enable_context instance-attribute

_enable_context = enable_context

_enable_file_attach instance-attribute

_enable_file_attach = enable_file_attach

_file_accept_types instance-attribute

_file_accept_types = file_accept_types

_context_allowed_roots instance-attribute

_context_allowed_roots = [(str(resolve())) for r in context_allowed_roots] if context_allowed_roots else None

_widget instance-attribute

_widget: Any = None

_is_anywidget instance-attribute

_is_anywidget: bool = False

_threads instance-attribute

_threads: dict[str, list[MessageDict]] = {}

_thread_titles instance-attribute

_thread_titles: dict[str, str] = {}

_cancel_events instance-attribute

_cancel_events: dict[str, Event] = {}

_settings_values instance-attribute

_settings_values: dict[str, Any] = {(id): (value) for s in (_settings_items) if type != 'separator'}

_pending_inputs instance-attribute

_pending_inputs: dict[str, dict[str, Any]] = {}

_aggrid_assets_sent instance-attribute

_aggrid_assets_sent: bool = include_aggrid

_plotly_assets_sent instance-attribute

_plotly_assets_sent: bool = include_plotly

_tradingview_assets_sent instance-attribute

_tradingview_assets_sent: bool = False

_context_sources instance-attribute

_context_sources: dict[str, dict[str, Any]] = {}

_session_id instance-attribute

_session_id: str = ''

_active_thread instance-attribute

_active_thread: str = default_id

active_thread_id property

active_thread_id: str

The currently active thread ID.

settings property

settings: dict[str, Any]

Current settings values.

threads property

threads: dict[str, list[MessageDict]]

Thread history (read-only view).

_STREAM_FLUSH_INTERVAL class-attribute instance-attribute

_STREAM_FLUSH_INTERVAL: float = 0.03

_STREAM_MAX_BUFFER class-attribute instance-attribute

_STREAM_MAX_BUFFER: int = 300

Functions

register_context_source

register_context_source(component_id: str, name: str) -> None

Register a live component as an @-mentionable context source.

Parameters:

Name Type Description Default
component_id str

Unique ID of the component.

required
name str

Human-readable label shown in the popup.

required

bind

bind(widget: Any) -> None

Bind to a widget after app.show().

Parameters:

Name Type Description Default
widget Any

The widget instance returned by app.show().

required

toolbar

toolbar(*, position: Literal['header', 'footer', 'top', 'bottom', 'left', 'right', 'inside'] = 'right') -> Any

Build a Toolbar containing the chat panel.

Returns:

Type Description
Any

A Toolbar instance for app.show(toolbars=...).

callbacks

callbacks() -> dict[str, Callable[..., Any]]

Return the callbacks dict for app.show(callbacks=...).

Returns:

Type Description
dict[str, Callable]

Event handler mappings for all chat events.

send_message

send_message(text: str, thread_id: str | None = None) -> None

Send a programmatic assistant message.

Parameters:

Name Type Description Default
text str

Message text.

required
thread_id str | None

Target thread. Defaults to active thread.

None

_emit

_emit(event: str, data: dict[str, Any]) -> None

Emit an event via the bound widget.

_emit_fire

_emit_fire(event: str, data: dict[str, Any]) -> None

Fire-and-forget emit for high-frequency streaming.

_inject_aggrid_assets

_inject_aggrid_assets() -> None

Lazy-inject AG Grid JS/CSS on first table artifact.

_inject_plotly_assets

_inject_plotly_assets() -> None

Lazy-inject Plotly JS on first plotly artifact.

_inject_tradingview_assets

_inject_tradingview_assets() -> None

Lazy-inject TradingView lightweight-charts on first TV artifact.

_dispatch_artifact

_dispatch_artifact(item: _ArtifactBase, message_id: str, thread_id: str) -> None

Dispatch an artifact to the frontend.

Parameters:

Name Type Description Default
item _ArtifactBase

Concrete artifact instance.

required
message_id str

Current assistant message ID.

required
thread_id str

Current thread ID.

required

_is_accepted_file

_is_accepted_file(filename: str) -> bool

Check if file extension is allowed.

_resolve_widget_attachment

_resolve_widget_attachment(widget_id: str, content: str | None = None, name: str | None = None) -> Attachment | None

Create an Attachment for a widget/component reference.

The resulting attachment always carries an explicit widget_id: <id> line so an LLM agent reading the @-context can use it directly in any tool call that requires a widget_id (notably the MCP tvchart_* family). Any payload returned by the JS-side getData() is appended after the id header.

_auto_attach_context_sources

_auto_attach_context_sources(existing: list[Attachment]) -> list[Attachment]

Append every registered context source the user didn't @-mention.

A widget registered with 🇵🇾meth:register_context_source is part of the chat's permanent @context: every user message carries an attachment for it whose content begins with widget_id: <id>. That guarantees an LLM agent always has the routing information for the chat's components without the user having to repeat @<name> on every turn.

Already-resolved attachments take precedence — explicit mentions keep whatever payload the frontend's getData() returned.

_resolve_attachments

_resolve_attachments(raw_attachments: list[dict[str, Any]]) -> list[Attachment]

Resolve raw attachment dicts into Attachment objects.

_get_context_sources

_get_context_sources() -> list[dict[str, str]]

List available context sources for the @-mention popup.

_dispatch_text_update

_dispatch_text_update(update: ThinkingUpdate | StatusUpdate | CitationUpdate, state: _StreamState, thread_id: str) -> None

Dispatch thinking, status, or citation updates.

_dispatch_config_update

_dispatch_config_update(update: CommandsUpdate | ConfigOptionUpdate | ModeUpdate, state: _StreamState) -> None

Dispatch commands, config, or mode updates.

_dispatch_tool_call_update

_dispatch_tool_call_update(update: ToolCallUpdate, state: _StreamState, thread_id: str) -> None

Dispatch a tool-call start OR a tool-result to the UI.

_dispatch_session_update

_dispatch_session_update(update: Any, state: _StreamState, thread_id: str, ctx: ChatContext | None) -> None

Dispatch a single SessionUpdate to the frontend.

_process_handler_item

_process_handler_item(item: Any, state: _StreamState, thread_id: str, ctx: ChatContext | None) -> None

Dispatch a handler yield item.

Handles plain strings, SessionUpdate objects, and artifacts.

_flush_buffer

_flush_buffer(state: _StreamState) -> None

Flush buffered text to the frontend.

_buffer_text

_buffer_text(state: _StreamState, text: str) -> None

Add text to buffer, auto-flush on threshold.

_finalize_stream

_finalize_stream(state: _StreamState, thread_id: str) -> None

Flush remaining buffer and send stream-done events.

_handle_cancel

_handle_cancel(state: _StreamState, thread_id: str) -> None

Handle stream cancellation.

_handle_stream

_handle_stream(gen: Any, message_id: str, thread_id: str, cancel: Event, *, ctx: ChatContext | None = None) -> None

Stream from a sync generator.

_handle_async_stream async

_handle_async_stream(agen: Any, message_id: str, thread_id: str, cancel: Event, *, ctx: ChatContext | None = None) -> None

Stream from an async generator.

_inject_context

_inject_context(messages: list[MessageDict], ctx: ChatContext, message_id: str, thread_id: str) -> list[MessageDict]

Inject attachment context into messages.

_dispatch_handler_result

_dispatch_handler_result(result: Any, message_id: str, thread_id: str, cancel: Event, ctx: ChatContext) -> None

Route handler return value to processing path.

_handle_complete

_handle_complete(text: str, message_id: str, thread_id: str) -> None

Send a complete (non-streamed) assistant message.

_run_handler

_run_handler(messages: list[MessageDict], ctx: ChatContext, message_id: str, thread_id: str, cancel: Event) -> None

Execute the handler in a background thread.

_on_user_message

_on_user_message(data: Any, _event_type: str, _label: str) -> None

Handle incoming user message.

_run_provider

_run_provider(messages: list[MessageDict], ctx: ChatContext, message_id: str, thread_id: str, cancel: Event) -> None

Execute the ACP provider prompt in a background thread.

_on_stop_generation

_on_stop_generation(data: Any, _event_type: str, _label: str) -> None

Cancel active generation.

_truncate_thread_at

_truncate_thread_at(thread_id: str, message_id: str, *, keep_target: bool) -> tuple[list[MessageDict], list[str]]

Truncate a thread's history at the message with message_id.

Returns a tuple (removed_messages, removed_ids). When keep_target is True the message itself is retained and only messages after it are removed; when False the target message and everything after it are removed.

_truncate_provider_state

_truncate_provider_state(thread_id: str, kept_messages: list[MessageDict]) -> None

Best-effort: align the provider's persisted history with the UI.

For LangGraph-backed providers (DeepagentProvider) we delete the thread state from the checkpointer so the next prompt rebuilds from a fresh state. The next user message gets re-sent to the agent with the full surviving history via the ChatManager flow.

_on_edit_message

_on_edit_message(data: Any, _event_type: str, _label: str) -> None

Edit a previously-sent user message and regenerate from there.

_on_resend_from

_on_resend_from(data: Any, _event_type: str, _label: str) -> None

Re-run generation from a specific user message in the thread.

The target user message stays visible — only the assistant reply (and any subsequent turns) are dropped and regenerated. Previous behaviour also removed the user bubble itself, which looked like the message had been erased.

_on_todo_clear

_on_todo_clear(_data: Any, _event_type: str, _label: str) -> None

Handle user clearing the plan/todo list.

_on_input_response

_on_input_response(data: Any, _event_type: str, _label: str) -> None

Handle user response to an input-required prompt.

_on_slash_command_event

_on_slash_command_event(data: Any, _event_type: str, _label: str) -> None

Handle slash command from frontend.

_on_thread_create

_on_thread_create(data: Any, _event_type: str, _label: str) -> None

Create a new thread.

_on_thread_switch

_on_thread_switch(data: Any, _event_type: str, _label: str) -> None

Switch to an existing thread.

_on_thread_delete

_on_thread_delete(data: Any, _event_type: str, _label: str) -> None

Delete a thread.

_on_thread_rename

_on_thread_rename(data: Any, _event_type: str, _label: str) -> None

Rename a thread.

_on_settings_change_event

_on_settings_change_event(data: Any, _event_type: str, _label: str) -> None

Handle settings change.

_on_request_state

_on_request_state(_data: Any, _event_type: str, _label: str) -> None

Respond to initialization request from frontend JS.

_build_thread_list

_build_thread_list() -> list[dict[str, str]]

Build thread list dicts for the frontend.

pywry.chat.manager.ChatContext dataclass

ChatContext(thread_id: str = '', message_id: str = '', settings: dict[str, Any] = dict(), cancel_event: Event = Event(), system_prompt: str = '', model: str = '', temperature: float = 0.7, attachments: list[Attachment] = list())

Context object passed to handler functions.

Attributes:

Name Type Description
thread_id str

Active thread ID.

message_id str

The assistant message ID being generated.

settings dict[str, Any]

Current settings values.

cancel_event Event

Set when the user clicks Stop.

system_prompt str

System prompt configured for the chat.

model str

Model name configured for the chat.

temperature float

Temperature configured for the chat.

attachments list[Attachment]

Resolved context attachments for the current message.

Attributes

thread_id class-attribute instance-attribute

thread_id: str = ''

message_id class-attribute instance-attribute

message_id: str = ''

settings class-attribute instance-attribute

settings: dict[str, Any] = field(default_factory=dict)

cancel_event class-attribute instance-attribute

cancel_event: Event = field(default_factory=Event)

system_prompt class-attribute instance-attribute

system_prompt: str = ''

model class-attribute instance-attribute

model: str = ''

temperature class-attribute instance-attribute

temperature: float = 0.7

attachments class-attribute instance-attribute

attachments: list[Attachment] = field(default_factory=list)

attachment_summary property

attachment_summary: str

One-line summary of attached context for system/user prompts.

Returns:

Type Description
str

Summary line, or empty string when no attachments.

context_text property

context_text: str

Pre-formatted attachment content ready to inject into prompts.

Returns:

Type Description
str

Multi-block attachment context, or empty string.

_input_event class-attribute instance-attribute

_input_event: Event = field(default_factory=Event, init=False, repr=False)

_input_response class-attribute instance-attribute

_input_response: str = field(default='', init=False, repr=False)

Functions

get_attachment

get_attachment(name: str) -> str

Retrieve attachment content or path by name.

Parameters:

Name Type Description Default
name str

Attachment name, with or without a leading @.

required

Returns:

Type Description
str

Attachment path or content, or a not-found message.

wait_for_input

wait_for_input(timeout: float | None = None) -> str

Block until the user provides input.

Parameters:

Name Type Description Default
timeout float | None

Maximum seconds to wait. None waits indefinitely.

None

Returns:

Type Description
str

User-supplied input, or empty string on cancellation/timeout.


ACP Session Updates

pywry.chat.updates.AgentMessageUpdate

Bases: BaseModel

Streaming text chunk from the agent.

Attributes

session_update class-attribute instance-attribute

session_update: Literal['agent_message'] = Field(default='agent_message', alias='sessionUpdate')

text class-attribute instance-attribute

text: str = ''

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

pywry.chat.updates.StatusUpdate

Bases: BaseModel

Transient status message shown inline in the UI.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['x_status'] = Field(default='x_status', alias='sessionUpdate')

text class-attribute instance-attribute

text: str = ''

pywry.chat.updates.ThinkingUpdate

Bases: BaseModel

Streaming thinking or reasoning chunk.

Rendered as a collapsible inline block. Thinking tokens are streamed in real-time and are not stored in conversation history.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['x_thinking'] = Field(default='x_thinking', alias='sessionUpdate')

text class-attribute instance-attribute

text: str = ''

pywry.chat.updates.CitationUpdate

Bases: BaseModel

Citation or source reference attached to a response.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['x_citation'] = Field(default='x_citation', alias='sessionUpdate')

url class-attribute instance-attribute

url: str = ''

title class-attribute instance-attribute

title: str = ''

snippet class-attribute instance-attribute

snippet: str = ''

pywry.chat.updates.ToolCallUpdate

Bases: BaseModel

Tool invocation notification or status update.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['tool_call'] = Field(default='tool_call', alias='sessionUpdate')

tool_call_id class-attribute instance-attribute

tool_call_id: str = Field(default='', alias='toolCallId')

title class-attribute instance-attribute

title: str = ''

name class-attribute instance-attribute

name: str = ''

kind class-attribute instance-attribute

kind: str = 'other'

status class-attribute instance-attribute

status: str = 'pending'

content class-attribute instance-attribute

content: Any = None

locations class-attribute instance-attribute

locations: Any = None

pywry.chat.updates.PlanUpdate

Bases: BaseModel

Agent execution plan.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['plan'] = Field(default='plan', alias='sessionUpdate')

entries class-attribute instance-attribute

entries: list[PlanEntry] = Field(default_factory=list)

pywry.chat.updates.CommandsUpdate

Bases: BaseModel

Available slash commands from the agent.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['available_commands'] = Field(default='available_commands', alias='sessionUpdate')

commands class-attribute instance-attribute

commands: list[ACPCommand] = Field(default_factory=list)

pywry.chat.updates.ConfigOptionUpdate

Bases: BaseModel

Config option state change.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['config_option'] = Field(default='config_option', alias='sessionUpdate')

options class-attribute instance-attribute

options: list[SessionConfigOption] = Field(default_factory=list)

pywry.chat.updates.ModeUpdate

Bases: BaseModel

Agent mode change notification.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['current_mode'] = Field(default='current_mode', alias='sessionUpdate')

current_mode_id class-attribute instance-attribute

current_mode_id: str = Field(default='', alias='currentModeId')

available_modes class-attribute instance-attribute

available_modes: list[SessionMode] = Field(default_factory=list, alias='availableModes')

pywry.chat.updates.PermissionRequestUpdate

Bases: BaseModel

Agent requests user permission before tool execution.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['permission_request'] = Field(default='permission_request', alias='sessionUpdate')

tool_call_id class-attribute instance-attribute

tool_call_id: str = Field(default='', alias='toolCallId')

title class-attribute instance-attribute

title: str = ''

options class-attribute instance-attribute

options: list[PermissionOption] = Field(default_factory=list)

request_id class-attribute instance-attribute

request_id: str = Field(default='', alias='requestId')

pywry.chat.updates.ArtifactUpdate

Bases: BaseModel

Rich artifact rendered in the chat UI.

The artifact field contains a concrete artifact model from chat.artifacts.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

session_update class-attribute instance-attribute

session_update: Literal['x_artifact'] = Field(default='x_artifact', alias='sessionUpdate')

artifact class-attribute instance-attribute

artifact: Any = None

Artifacts

pywry.chat.artifacts.CodeArtifact

Bases: _ArtifactBase

Code snippet rendered with syntax highlighting.

Attributes:

Name Type Description
artifact_type str

Fixed to "code".

content str

Source code or text snippet.

language str

Language hint for syntax highlighting.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['code'] = 'code'

content class-attribute instance-attribute

content: str = ''

language class-attribute instance-attribute

language: str = ''

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.MarkdownArtifact

Bases: _ArtifactBase

Markdown content rendered as formatted HTML.

Attributes:

Name Type Description
artifact_type str

Fixed to "markdown".

content str

Markdown source.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['markdown'] = 'markdown'

content class-attribute instance-attribute

content: str = ''

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.HtmlArtifact

Bases: _ArtifactBase

Raw HTML rendered in a sandboxed container.

Attributes:

Name Type Description
artifact_type str

Fixed to "html".

content str

Raw HTML content.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['html'] = 'html'

content class-attribute instance-attribute

content: str = ''

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.TableArtifact

Bases: _ArtifactBase

Tabular data rendered as an AG Grid widget.

Accepts the same data formats as normalize_data() in grid.py: pandas DataFrame, list of dicts, dict of lists, or single dict.

Attributes:

Name Type Description
artifact_type str

Fixed to "table".

data list[dict[str, Any]] | dict[str, Any]

Table rows or source object to normalize.

column_defs list[dict[str, Any]] | None

Optional AG Grid column definitions.

grid_options dict[str, Any] | None

Optional AG Grid configuration overrides.

height str

CSS height for the table container.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['table'] = 'table'

data class-attribute instance-attribute

data: list[dict[str, Any]] | dict[str, Any] = Field(default_factory=list)

column_defs class-attribute instance-attribute

column_defs: list[dict[str, Any]] | None = None

grid_options class-attribute instance-attribute

grid_options: dict[str, Any] | None = None

height class-attribute instance-attribute

height: str = '400px'

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.PlotlyArtifact

Bases: _ArtifactBase

Plotly chart rendered as an interactive widget.

figure accepts a standard Plotly figure dict: {"data": [...traces], "layout": {...}, "config": {...}}.

Attributes:

Name Type Description
artifact_type str

Fixed to "plotly".

figure dict[str, Any]

Plotly figure payload.

height str

CSS height for the chart container.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['plotly'] = 'plotly'

figure class-attribute instance-attribute

figure: dict[str, Any] = Field(default_factory=dict)

height class-attribute instance-attribute

height: str = '400px'

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.ImageArtifact

Bases: _ArtifactBase

Image rendered as an <img> element.

url can be a data URI (data:image/png;base64,...) or an HTTP(S) URL.

Attributes:

Name Type Description
artifact_type str

Fixed to "image".

url str

Image URL or data URI.

alt str

Alternate text.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['image'] = 'image'

url class-attribute instance-attribute

url: str = ''

alt class-attribute instance-attribute

alt: str = ''

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

Functions

_block_dangerous_schemes classmethod

_block_dangerous_schemes(v: str) -> str

Reject javascript: URLs.

Parameters:

Name Type Description Default
v str

Candidate image URL.

required

Returns:

Type Description
str

The original URL when safe.

Raises:

Type Description
ValueError

When the URL uses the javascript: scheme.

pywry.chat.artifacts.JsonArtifact

Bases: _ArtifactBase

Structured data rendered as a collapsible JSON tree.

Attributes:

Name Type Description
artifact_type str

Fixed to "json".

data Any

Arbitrary JSON-serializable payload.

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['json'] = 'json'

data class-attribute instance-attribute

data: Any = None

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.TradingViewArtifact

Bases: _ArtifactBase

Interactive financial chart via TradingView lightweight-charts.

Supports candlestick, line, area, bar, baseline, and histogram series. Multiple series can be overlaid on a single chart.

Attributes:

Name Type Description
artifact_type str

Fixed to "tradingview".

series list[TradingViewSeries]

One or more data series to render.

options dict[str, Any]

Chart-level options passed to createChart() (layout, grid, crosshair, timeScale, rightPriceScale, etc.).

height str

CSS height for the chart container.

Examples:

>>> yield TradingViewArtifact(
...     title="AAPL Daily",
...     series=[
...         TradingViewSeries(
...             type="candlestick",
...             data=[
...                 {
...                     "time": "2024-01-02",
...                     "open": 185.5,
...                     "high": 186.1,
...                     "low": 184.0,
...                     "close": 185.6,
...                 },
...             ],
...         ),
...     ],
...     height="500px",
... )

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['tradingview'] = 'tradingview'

series class-attribute instance-attribute

series: list[TradingViewSeries] = Field(default_factory=list)

options class-attribute instance-attribute

options: dict[str, Any] = Field(default_factory=dict)

height class-attribute instance-attribute

height: str = '400px'

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

pywry.chat.artifacts.TradingViewSeries

Bases: BaseModel

A single data series on a TradingView chart.

Attributes:

Name Type Description
type str

Series type — "candlestick", "line", "area", "bar", "baseline", or "histogram".

data list[dict[str, Any]]

Data points. For candlestick: {"time", "open", "high", "low", "close"}. For line/area/histogram: {"time", "value"}.

options dict[str, Any]

Series-level options (colors, line width, price format, etc.).

markers list[dict[str, Any]] | None

Optional markers (buy/sell signals, annotations).

Attributes

type class-attribute instance-attribute

type: Literal['candlestick', 'line', 'area', 'bar', 'baseline', 'histogram'] = 'candlestick'

data class-attribute instance-attribute

data: list[dict[str, Any]] = Field(default_factory=list)

options class-attribute instance-attribute

options: dict[str, Any] = Field(default_factory=dict)

markers class-attribute instance-attribute

markers: list[dict[str, Any]] | None = None

pywry.chat.artifacts.AppArtifact

Bases: _ArtifactBase

Full PyWry widget rendered inline as a sandboxed iframe.

Unlike :class:HtmlArtifact which carries only raw HTML, an AppArtifact represents a complete PyWry app snapshot — inlined CSS / JS / data — and optionally carries a live widget_id + revision pair so the iframe can open a WebSocket bridge back to the Python runtime for event traffic. When a new revision of the same widget_id is emitted, older revisions close their bridge server-side and the iframe stays frozen at its last known state while remaining readable in chat history.

Attributes:

Name Type Description
artifact_type str

Fixed to "app".

html str

Self-contained HTML document (CSS / JS / data inlined).

widget_id str | None

Optional backend identifier for live event wiring. When set, the iframe opens a WebSocket carrying revision so the server can reject stale renders.

revision int

Monotonic render counter, incremented each time the widget is re-rendered via the MCP layer. 0 means "no live bridge — treat as a static snapshot".

height str

CSS height for the iframe container.

sandbox bool

If True (default), the iframe is loaded with sandbox="allow-scripts allow-same-origin".

Attributes

artifact_type class-attribute instance-attribute

artifact_type: Literal['app'] = 'app'

html class-attribute instance-attribute

html: str = ''

widget_id class-attribute instance-attribute

widget_id: str | None = None

revision class-attribute instance-attribute

revision: int = 0

height class-attribute instance-attribute

height: str = '600px'

sandbox class-attribute instance-attribute

sandbox: bool = True

type class-attribute instance-attribute

type: Literal['artifact'] = 'artifact'

title class-attribute instance-attribute

title: str = ''

ACP Session Primitives

pywry.chat.session.SessionMode

Bases: BaseModel

An operational mode advertised by the agent.

Modes affect system prompts, tool availability, and permission requirements. Common examples: Ask, Architect, Code.

Attributes:

Name Type Description
id str

Unique identifier for the mode.

name str

Human-readable title.

description str | None

Optional explanation of mode behaviour.

Attributes

id instance-attribute

id: str

name instance-attribute

name: str

description class-attribute instance-attribute

description: str | None = None

pywry.chat.session.SessionConfigOption

Bases: BaseModel

A configurable, session-level setting exposed by the agent.

Attributes:

Name Type Description
id str

Unique identifier for the option.

name str

Human-readable label.

description str | None

Optional details about the option's purpose.

category str | None

Semantic category — "mode", "model", "thought_level", or a custom string.

type str

Currently only "select" is supported by ACP.

currentValue str

The active setting value.

options list[ConfigOptionChoice]

Available choices.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

id instance-attribute

id: str

name instance-attribute

name: str

description class-attribute instance-attribute

description: str | None = None

category class-attribute instance-attribute

category: str | None = None

type class-attribute instance-attribute

type: Literal['select'] = 'select'

current_value class-attribute instance-attribute

current_value: str = Field(default='', alias='currentValue')

options class-attribute instance-attribute

options: list[ConfigOptionChoice] = Field(default_factory=list)

pywry.chat.session.PlanEntry

Bases: BaseModel

A single entry in an agent execution plan.

Attributes:

Name Type Description
content str

Human-readable description of the task.

priority str

Importance level — "high", "medium", or "low".

status str

Current state — "pending", "in_progress", or "completed".

Attributes

content instance-attribute

content: str

priority class-attribute instance-attribute

priority: Literal['high', 'medium', 'low'] = 'medium'

status class-attribute instance-attribute

status: Literal['pending', 'in_progress', 'completed'] = 'pending'

pywry.chat.session.PermissionRequest

Bases: BaseModel

Agent request for user permission before tool execution.

Attributes:

Name Type Description
toolCallId str

The tool call requiring permission.

title str

Human-readable description of what the tool will do.

options list[PermissionOption]

Available user choices.

request_id str

Stable identifier for correlating the response.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

tool_call_id class-attribute instance-attribute

tool_call_id: str = Field(alias='toolCallId')

title class-attribute instance-attribute

title: str = ''

options class-attribute instance-attribute

options: list[PermissionOption] = Field(default_factory=lambda: [PermissionOption(id='allow_once', label='Allow once'), PermissionOption(id='allow_always', label='Allow always'), PermissionOption(id='reject_once', label='Reject'), PermissionOption(id='reject_always', label='Always reject')])

request_id class-attribute instance-attribute

request_id: str = Field(default_factory=lambda: f'perm_{hex[:8]}')

pywry.chat.session.ClientCapabilities

Bases: BaseModel

Capabilities the client advertises during initialize.

Attributes:

Name Type Description
fileSystem bool

Client supports fs/read_text_file and fs/write_text_file methods.

terminal bool

Client supports terminal/* methods.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

file_system class-attribute instance-attribute

file_system: bool = Field(default=False, alias='fileSystem')

terminal class-attribute instance-attribute

terminal: bool = False

pywry.chat.session.AgentCapabilities

Bases: BaseModel

Capabilities the agent advertises during initialize.

Attributes:

Name Type Description
promptCapabilities PromptCapabilities | None

Supported prompt content types.

loadSession bool

Agent supports session/load.

configOptions bool

Agent supports session/set_config_option.

modes bool

Agent supports session/set_mode.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

prompt_capabilities class-attribute instance-attribute

prompt_capabilities: PromptCapabilities | None = Field(default=None, alias='promptCapabilities')

load_session class-attribute instance-attribute

load_session: bool = Field(default=False, alias='loadSession')

config_options class-attribute instance-attribute

config_options: bool = Field(default=False, alias='configOptions')

modes class-attribute instance-attribute

modes: bool = False

Content Models

pywry.chat.models.ACPToolCall

Bases: BaseModel

ACP tool invocation attached to an assistant message.

Attributes:

Name Type Description
toolCallId str

Unique identifier within the session.

title str

Human-readable description shown in the UI.

name str

Tool name.

kind str

Category from the ACP taxonomy.

status str

Lifecycle state.

arguments dict[str, Any]

Tool arguments.

content list[ContentBlock] | None

Result content blocks (populated on completion).

locations list[ToolCallLocation] | None

Affected file paths with optional line numbers.

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

tool_call_id class-attribute instance-attribute

tool_call_id: str = Field(default_factory=lambda: f'call_{hex[:12]}', alias='toolCallId')

title class-attribute instance-attribute

title: str = ''

name class-attribute instance-attribute

name: str = ''

kind class-attribute instance-attribute

kind: ToolCallKind = 'other'

status class-attribute instance-attribute

status: ToolCallStatus = 'pending'

arguments class-attribute instance-attribute

arguments: dict[str, Any] = Field(default_factory=dict)

content class-attribute instance-attribute

content: list[ContentBlock] | None = None

locations class-attribute instance-attribute

locations: list[ToolCallLocation] | None = None

pywry.chat.models.ACPCommand

Bases: BaseModel

ACP slash command advertised by the agent.

Attributes:

Name Type Description
name str

Command name (e.g. "web", "test").

description str

Human-readable description.

input ACPCommandInput | None

Optional input hints.

Attributes

name instance-attribute

name: str

description class-attribute instance-attribute

description: str = ''

input class-attribute instance-attribute

input: ACPCommandInput | None = None

pywry.chat.models.ChatMessage

Bases: BaseModel

A single chat message.

Attributes:

Name Type Description
role str

Semantic role — "user", "assistant", "system", or "tool".

content str | list[ContentBlock]

Message body as plain text or ACP content blocks.

message_id str

Stable identifier used across UI and backend events.

timestamp float

Unix timestamp when the message was created.

metadata dict[str, Any]

Arbitrary provider- or application-specific metadata.

tool_calls list[ACPToolCall] | None

Tool invocations attached to assistant messages.

tool_call_id str | None

Tool-call identifier when this message is a tool result.

model str | None

Model name that produced the message.

usage dict[str, Any] | None

Token or billing metadata.

stopped bool

Whether generation stopped early (e.g. cancellation).

Attributes

role instance-attribute

role: Literal['user', 'assistant', 'system', 'tool']

content class-attribute instance-attribute

content: str | list[ContentBlock] = ''

message_id class-attribute instance-attribute

message_id: str = Field(default_factory=lambda: f'msg_{hex[:12]}')

timestamp class-attribute instance-attribute

timestamp: float = Field(default_factory=time)

metadata class-attribute instance-attribute

metadata: dict[str, Any] = Field(default_factory=dict)

tool_calls class-attribute instance-attribute

tool_calls: list[ACPToolCall] | None = None

tool_call_id class-attribute instance-attribute

tool_call_id: str | None = None

model class-attribute instance-attribute

model: str | None = None

usage class-attribute instance-attribute

usage: dict[str, Any] | None = None

stopped class-attribute instance-attribute

stopped: bool = False

model_config class-attribute instance-attribute

model_config = ConfigDict(populate_by_name=True)

Functions

validate_content_length classmethod

validate_content_length(v: str | list[ContentBlock]) -> str | list[ContentBlock]

Reject content exceeding MAX_CONTENT_LENGTH.

Parameters:

Name Type Description Default
v str | list[ContentBlock]

Candidate message content.

required

Returns:

Type Description
str | list[ContentBlock]

The original content when it satisfies size limits.

Raises:

Type Description
ValueError

When plain-text content exceeds MAX_CONTENT_LENGTH.

text_content

text_content() -> str

Return the plain-text content regardless of content type.

Returns:

Type Description
str

The plain-text message body, flattening structured text parts.

pywry.chat.models.ChatConfig

Bases: BaseModel

Configuration for the chat engine.

Attributes:

Name Type Description
system_prompt str | None

Optional system prompt prepended to model conversations.

model str

Default model identifier.

temperature float

Sampling temperature.

max_tokens int

Maximum token budget per generation.

streaming bool

Enable streaming responses.

persist bool

Persist chat history between sessions.

Attributes

system_prompt class-attribute instance-attribute

system_prompt: str | None = None

model class-attribute instance-attribute

model: str = 'gpt-4'

temperature class-attribute instance-attribute

temperature: float = Field(default=0.7, ge=0.0, le=2.0)

max_tokens class-attribute instance-attribute

max_tokens: int = Field(default=4096, ge=1)

streaming class-attribute instance-attribute

streaming: bool = True

persist class-attribute instance-attribute

persist: bool = False

UI State Models

pywry.chat.manager.SettingsItem

Bases: BaseModel

Settings menu item shown in the gear dropdown.

Attributes:

Name Type Description
id str

Stable identifier.

label str

User-visible label.

type str

Control type.

value Any

Current value.

options list[str] | None

Allowed values for select controls.

min float | None

Minimum for range controls.

max float | None

Maximum for range controls.

step float | None

Increment for range controls.

Attributes

id instance-attribute

id: str

label class-attribute instance-attribute

label: str = ''

type class-attribute instance-attribute

type: Literal['action', 'toggle', 'select', 'range', 'separator'] = 'action'

value class-attribute instance-attribute

value: Any = None

options class-attribute instance-attribute

options: list[str] | None = None

min class-attribute instance-attribute

min: float | None = None

max class-attribute instance-attribute

max: float | None = None

step class-attribute instance-attribute

step: float | None = None

RBAC

pywry.chat.permissions.check_acp_permission async

check_acp_permission(user_session: Any, widget_id: str, operation: str, session_store: Any) -> bool

Check RBAC permission for an ACP operation.

Parameters:

Name Type Description Default
user_session UserSession | None

Current user session. None means auth is disabled — all operations are permitted.

required
widget_id str

Widget scope for the permission check.

required
operation str

ACP operation name (e.g. "session/prompt").

required
session_store SessionStore

Store for permission lookups.

required

Returns:

Type Description
bool

Whether the operation is permitted.