llm_dart_chat 0.11.0-alpha.1
llm_dart_chat: ^0.11.0-alpha.1 copied to clipboard
Pure Dart chat session and transport abstractions for llm_dart.
llm_dart_chat #
Pure Dart chat session and transport abstractions for llm_dart.
This package owns the reusable chat runtime:
ChatSessionChatTransportDefaultChatSessionChatUiStreamReaderDirectChatTransportHttpChatTransport- snapshot and persistence codecs
- compatibility re-exports for chat-runtime-oriented imports
The persistence helper in this package is intentionally session-oriented:
ChatPersistenceAdapter.saveSnapshot(...)ChatPersistenceAdapter.saveSession(...)ChatPersistenceAdapter.restoreSession(...)
Runtime Boundary #
llm_dart_chat does not own provider model-call semantics. Direct chat
transport sends prompts through llm_dart_ai.streamText(...), then projects the
AI runtime full stream into ChatUiStreamChunk values.
New user input enters the session through ChatInput, which holds a
user-facing UserModelMessage. Ordinary DefaultChatSession(...)
construction also accepts app-facing initialMessages as ModelMessage
values. The session normalizes those inputs into provider-facing
PromptMessage values before appending prompt history. Existing prompt
history, transport payloads, and snapshots intentionally stay on
PromptMessage because they are replay state, not ordinary app input. Use
DefaultChatSession.withPromptHistory(...) only when you already have
provider-normalized replay prompt history.
The layers are:
- providers emit
LanguageModelStreamEventfor one model call llm_dart_aiemitsTextStreamEventfor a full run, including run/step lifecycle, local tools, aborts, and errorsllm_dart_chatconsumesChatUiStreamChunkand owns chat state, persistence, transport protocols, and manual tool output submission
Use DirectChatTransport when the client can call a model directly. Use
HttpChatTransport when routing, credentials, audit policy, or tool execution
should stay backend-owned.
ChatRequestOptions forwards local runtime options such as declared function
tools, toolChoice, functionToolExecutor, maxSteps, stopWhen, and
runtime callbacks through DirectChatTransport into streamText(...).
HttpChatTransport rejects those local-only fields for now because the HTTP
protocol does not serialize Dart callbacks or a remote tool registry. Put that
tool loop on the server side when using HTTP transport.
Flutter-only controller convenience stays in
package:llm_dart_flutter/llm_dart_flutter.dart.
It depends only on:
llm_dart_aillm_dart_transport
That makes it suitable for:
- CLI apps
- server-side Dart backends
- framework-neutral chat orchestration
- Flutter adapters that want to build on the same runtime layer
Recommended adoption order:
- start with
DefaultChatSessionplusDirectChatTransportwhen the app can call a concrete model directly; direct transport still streams throughllm_dart_ai.streamText(...)before projecting to chat UI chunks - switch to
HttpChatTransportwhen routing, keys, audit policy, or tool execution should stay backend-owned - use
ChatPersistenceAdapterwhen you need durable session snapshots - reach for
ChatUiStreamReaderonly when you already have aStream<ChatUiStreamChunk>and do not want the full session/runtime layer - move up to
llm_dart_flutteronly when the UI needsValueNotifier-style controller wiring
Direct Chunk Stream Reading #
If you already have a Stream<ChatUiStreamChunk> and do not want the full
DefaultChatSession, use:
ChatUiStreamReaderreadChatUiStream(...)
The reader keeps the same stream-first contract:
- projected persistent message snapshots on the main stream
stepEventsforStepStartEventandStepFinishEventboundariesstepFinishStreamfor the existing finish-only convenience pathtransientDataPartsfor runtime-onlydata-*signalsresultplus finish-state convenience futures for final inspection
It also supports optional validation hooks for direct chunk-stream consumers:
messageMetadataValidator- validates merged message metadata before start/patch/finish metadata chunks are applied
dataPartValidator- validates both persistent and transient
DataUiParts before projection or side-channel delivery
- validates both persistent and transient
Runnable Example #
For a framework-neutral session example that uses:
DefaultChatSessionDirectChatTransportToolExecutionRegistryChatPersistenceAdapter
see:
example/chat_runtime.dartexample/http_backend_hint_mapping.dart- shows how a client can send app-owned metadata through
HttpChatTransportwhile a backend maps those hints into provider-specific invocation options
- shows how a client can send app-owned metadata through
If you need Flutter ValueNotifier integration, use
package:llm_dart_flutter/llm_dart_flutter.dart, which wraps this package with
ChatController and a controller-aware persistence adapter.
HTTP Backend Hint Mapping #
HttpChatTransport deliberately stays provider-neutral. It serializes shared
prompt state, shared GenerateTextOptions, stream protocol selection, and
app-owned metadata. It does not serialize raw ProviderInvocationOptions.
Use metadata and prepareSendMessagesRequest when a client needs to pass
backend-owned routing hints:
final session = DefaultChatSession(
transport: HttpChatTransport(
endpoint: Uri.parse('https://backend.example/chat'),
transport: transportClient,
prepareSendMessagesRequest: (context) {
return HttpChatTransportPreparedSendMessagesRequest(
payload: HttpChatTransportRequestPayload(
chatId: context.payload.chatId,
prompt: context.payload.prompt,
generateOptions: context.payload.generateOptions,
streamProtocol: context.payload.streamProtocol,
metadata: {
...context.payload.metadata,
'providerProfile': 'openai-web-search',
},
),
);
},
),
);
The backend should decode HttpChatTransportRequestPayload, map app-owned
metadata such as providerProfile into typed provider options, and return SSE
frames encoded with HttpChatTransportServerAdapter.
Run the self-contained in-process demo with:
dart run example/http_backend_hint_mapping.dart
Message Mapping Layers #
ChatUiMessage and ChatUiPart remain the source of truth and are owned by
llm_dart_ai. Use ChatMessageMapper only when a CLI, server-rendered UI, or
framework-neutral adapter wants stable cross-provider summaries such as:
textreasoningTexttoolPartssourcesfilePartswarningserrors
ChatMessageMapper now lives in llm_dart_ai as part of the shared UI/runtime
layer and is re-exported here for chat-runtime users that prefer to stay on a
single package import path.
If a pure Dart application also needs provider-owned inspection, compose the
shared mapper with app-owned metadata inspection or provider custom-part
helpers instead of widening llm_dart_chat itself:
package:llm_dart_openai/llm_dart_openai.dartOpenAICustomPartandOpenAICustomPartSummaryfor provider-owned content parts and stream events before UI projection
package:llm_dart_google/llm_dart_google.dartGoogleCustomPartandGoogleCustomPartSummaryfor provider-owned custom prompt/content parts and stream events before UI projection
That keeps the runtime/session layer provider-neutral while still allowing rich provider-specific rendering where applications need it.