jido_chat is the core adapter contract and canonical data model for Jido.Chat integrations.
Release Status
jido_chat is published on Hex as part of the Jido 1.x chat package release
line.
Jido.Chat is an Elixir implementation aligned to the Vercel Chat SDK
(chat-sdk.dev/docs).
The package is intentionally scoped to the adapter layer:
jido_chatowns typed content/event models, adapter contracts, typed thread/channel handles, and deterministic fallback behavior.jido_messagingowns supervised runtime concerns such as webhook ingress, delivery queues, retries, room/session state, bridge lifecycle, and process trees.- A trusted application/runtime resolver owns stable identity resolution. In an
Author,idis the framework-neutral stable identity anduser_idis the provider identity. Adapters may pass anAuthor.idsupplied by that resolver, but they must not infer it from provider IDs, display names, usernames, or email addresses, and they must not call a provider to obtain it for this contract.
It provides:
Jido.Chatas a lightweight struct + event-loop facade for local/in-memory flows- typed thread and channel handles (
Thread,ChannelRef) - canonical outbound payloads (
Postable,PostPayload,FileUpload,StreamChunk) - rich content models (
Markdown,Card,Modal,ModalResponse) - typed normalized inbound/event payloads (
Incoming,Message,SentMessage,Response,EventEnvelope) - explicit adapter capability negotiation and fallback behavior (
Jido.Chat.Adapter,CapabilityMatrix) - lightweight state and concurrency hooks used by
Jido.Chattoday (StateAdapter,Concurrency) - framework-agnostic AI history conversion (
Jido.Chat.AI) - provider-free ExUnit adapter conformance support (
Jido.Chat.AdapterTestKit)
Installation
def deps do
[
{:jido_chat, "~> 1.0"}
]
endRun mix deps.get after adding the dependency.
Canonical Adapter Interface
Jido.Chat.Adapter is the canonical contract for new integrations.
Jido.Chat.ChannelRef and Jido.Chat.Thread are the typed handles for room and thread operations.
Adapters can expose native rich posting through post_message/3, which receives the full
typed Jido.Chat.PostPayload including attachments. send_file/3 remains the low-level
upload hook used by the core fallback path for single-upload posts.
Adapter Author Checklist
- Implement the required
Jido.Chat.Adaptercallbacks for your transport. - Declare explicit surface support through
capabilities/0instead of relying on callback inference. - If you build directly on the lightweight
Jido.Chatfacade and ship a customJido.Chat.StateAdapter, implementlock/5,release_lock/3, andforce_release_lock/2, and persistlockspluspending_locksin snapshots. To support bounded queue and burst controls, also implement the optionallock_with_options/6anddrain_lock/5callbacks. Existing adapters can continue to use the original callbacks. - Treat
Jido.Chat.PostPayloadas the canonical outbound contract. It can now carry text, markdown, raw payloads, cards, streams, attachments, andFileUploadvalues. - Preserve compatibility with legacy inbound payloads. Existing messages, wire maps, and adapter payloads do not need
authoror reply fields; normalization keeps their useful values and leaves the enriched fields unset. - Treat
Author.idas trusted, framework-neutral stable identity. Only pass it through when an application/runtime resolver supplied it. Never derive it from a provider ID, display name, username, or email, and do not make provider profile calls to resolve it for this contract. - Reply context is shallow and uses only data already present in the event. It does not perform replied-message lookup.
- Run
mix qualitybefore publishing adapter changes.
See the Adapter Test Kit guide for the reusable conformance case, deterministic factories and mocks, and provider-extension tests.
Message lifecycle events
Adapter parse_event/2 implementations must map provider edit and delete
deliveries to :message_updated and :message_deleted envelopes. Their typed
payloads are Jido.Chat.MessageUpdatedEvent and
Jido.Chat.MessageDeletedEvent. Each payload must include the provider
message_id. It should also include adapter, channel, thread, author,
timestamp, metadata, and raw provider context when these values are available.
An update should put the new content in message. A delete uses message: nil
when the provider does not send the deleted content. Core does not fetch or
recover that content. Lifecycle handlers use on_message_updated/2 and
on_message_deleted/2. They are separate from on_new_message/3, so provider
edit deliveries, including bot streaming edits, do not start a new-message
handler loop.
Core normalizes and routes each lifecycle delivery. Duplicate-delivery policy, persisted-message lookup, and the result for an unknown persisted message ID belong to the consuming runtime or persistence layer.
Usage (Core Loop)
chat =
Jido.Chat.new(
user_name: "jido",
adapters: %{telegram: Jido.Chat.Telegram.Adapter}
)
|> Jido.Chat.on_new_mention(fn thread, incoming ->
Jido.Chat.Thread.post(thread, "hi #{incoming.display_name || "there"}")
end)Additional Core Helpers
ai_messages = Jido.Chat.AI.to_messages(history, include_names: true)
payload =
Jido.Chat.PostPayload.new(%{
text: "Hello",
files: [%{path: "/tmp/report.pdf", filename: "report.pdf"}]
})Scope Notes
Jido.Chatincludes lightweight subscription/state/concurrency hooks today so the core facade can run locally without a larger runtime.- Burst and debounce timing is caller-driven. The core stores bounded pending state and exposes
drain_lock/4; it does not start a timer or a supervised process. - Production ingress, retries, room/session state, and supervised delivery orchestration belong in
jido_messaging, not this package. - The AI conversion helpers are structurally compatible with Chat SDK / AI SDK message shapes, but they keep Elixir-native naming and callback conventions.
Reference Docs
The package-level parity matrix and migration notes are tracked in the
proj_jido_chat workspace while this package is moving through the 1.x release
batch.