AI Providers¶
AIProvider ¶
Bases: ABC
AI model provider for generating responses.
supports_streaming
property
¶
Whether this provider supports streaming token generation.
supports_structured_streaming
property
¶
Whether this provider supports structured streaming with tool calls.
model_name
abstractmethod
property
¶
Model identifier (e.g. 'claude-sonnet-4-20250514', 'gpt-4o').
context_window
property
¶
Input context window of the active model in tokens, if known.
Resolved offline from the curated :meth:available_models catalog
keyed by :attr:model_name — no API key or network. Returns None
when the active model is absent from the catalog (custom / local model
ids, e.g. an arbitrary vLLM model string), so callers must degrade
gracefully rather than assume a window.
available_models
classmethod
¶
Curated, offline catalog of models known to this provider.
Returns a hand-maintained list — no API key or network required — so an integrator can discover configurable models by calling this on the class before instantiating. The base returns an empty list; each provider overrides it with its catalog.
list_models
async
¶
Models reported live by the provider's API.
The base implementation returns the curated :meth:available_models.
Providers whose API exposes a models endpoint override this to query
it, backfilling missing metadata from the catalog via
:meth:_merge_curated.
generate
abstractmethod
async
¶
Generate an AI response from the given context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
AIContext
|
Conversation context including messages, system prompt, temperature, and target channel capabilities. |
required |
Returns:
| Type | Description |
|---|---|
AIResponse
|
The AI response with content, usage stats, and optional |
AIResponse
|
tasks/observations. |
generate_stream
async
¶
Yield text deltas as they arrive. Override for streaming providers.
generate_structured_stream
async
¶
Yield structured events (thinking deltas, text deltas, tool calls, done).
Default implementation wraps generate() so every provider works
without changes. Override for true streaming support.
AIContext ¶
Bases: BaseModel
Context passed to AI provider for generation.
AIMessage ¶
Bases: BaseModel
A message in the AI conversation context.
AITextPart ¶
Bases: BaseModel
Text part of a multimodal message.
AIImagePart ¶
Bases: BaseModel
Image part of a multimodal message.
AITool ¶
Bases: BaseModel
Tool definition for function calling.
AIToolCall ¶
Bases: BaseModel
A tool call from the AI response.
AIResponse ¶
Bases: BaseModel
Response from an AI provider.
MockAIProvider ¶
Bases: AIProvider
Round-robin response provider for tests.
Per-Room AI Configuration¶
AI channel settings can be overridden per-room using binding metadata:
# Default AI channel
ai = AIChannel("ai", provider=anthropic, system_prompt="Default assistant")
kit.register_channel(ai)
# Override per room
kit.attach_channel(room_id, "ai", metadata={
"system_prompt": "You are a customer support agent for Acme Corp.",
"temperature": 0.3, # More deterministic
"max_tokens": 2048,
})
Silent Observer Pattern (Meeting Notes)¶
# Attach AI as note-taker
kit.attach_channel(meeting_room_id, "ai", metadata={
"system_prompt": """You are a meeting note-taker.
Listen to the conversation silently.
When someone says 'meeting ended', compile and send a summary.""",
})
# Mute so AI listens but doesn't respond
await kit.mute(meeting_room_id, "ai")
# Later, unmute to let AI send summary
await kit.unmute(meeting_room_id, "ai")
Tools/Function Calling¶
Tools can be passed via binding metadata for function calling:
kit.attach_channel(room_id, "ai", metadata={
"tools": [
{
"name": "search_knowledge_base",
"description": "Search the company knowledge base",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
]
})
Tool calls are returned in AIResponse.tool_calls:
response = await provider.generate(context)
for tool_call in response.tool_calls:
print(f"Tool: {tool_call.name}, Args: {tool_call.arguments}")
Gemini Provider¶
GeminiAIProvider ¶
Bases: AIProvider
AI provider using the Google Gemini API.
GeminiConfig ¶
Bases: BaseModel
Google Gemini AI provider configuration.
thinking_level
class-attribute
instance-attribute
¶
Thinking level for Gemini 3.1 models: minimal, low, medium, high.
Usage¶
from roomkit.providers.gemini.ai import GeminiAIProvider
from roomkit.providers.gemini.config import GeminiConfig
config = GeminiConfig(api_key="your-api-key")
provider = GeminiAIProvider(config)
# Use with AIChannel
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[gemini]
Schema Cleaning¶
Gemini rejects extra JSON Schema fields common in MCP/OpenAPI tool definitions. RoomKit auto-cleans schemas when building FunctionDeclaration objects:
clean_gemini_schema ¶
Recursively prepare a JSON Schema for Gemini FunctionDeclaration.
Performs two passes per node:
- Collapse Pydantic-style Optionals.
{"anyOf": [{...}, {"type": "null"}]}becomes{..., "nullable": true}.oneOf/allOfare handled the same way for symmetry. When the union has multiple non-null branches, we keep the first one and addnullableif any branch was null — Gemini's schema dialect does not support full union types. - Strip unknown keys. Anything not in :data:
_GEMINI_ALLOWED_KEYS(e.g.$schema,additionalProperties,default,title) is dropped at every nesting level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
dict[str, Any] | None
|
A JSON Schema dict (e.g. tool parameter schema), or None. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
A cleaned copy, or None if input was None. |
vLLM Provider (Local LLM)¶
VLLMConfig ¶
Bases: BaseModel
Configuration for a local vLLM server.
vLLM exposes an OpenAI-compatible API, so this config is translated
into an OpenAIConfig by :func:create_vllm_provider.
Attributes:
| Name | Type | Description |
|---|---|---|
model |
str
|
Model name loaded by the vLLM server (required). |
base_url |
str
|
Base URL of the vLLM OpenAI-compatible endpoint. |
api_key |
SecretStr
|
Bearer token sent as |
max_tokens |
int
|
Maximum tokens in the response. |
temperature |
float
|
Sampling temperature. |
timeout |
float
|
HTTP request timeout in seconds. Increase for vLLM servers that load models lazily on first request. |
headers |
dict[str, str] | None
|
Extra HTTP headers on every request — for a reverse proxy
that needs custom headers, or a non-Bearer |
extra_body |
dict[str, Any] | None
|
Extra JSON fields merged into every request body — the
route for vLLM-specific params ( |
max_retries
class-attribute
instance-attribute
¶
SDK-level retry count. Default 0 because RoomKit's RetryPolicy handles retries at the right layer with proper backoff and fallback.
include_stream_usage
class-attribute
instance-attribute
¶
When True, request token usage in streaming responses.
headers
class-attribute
instance-attribute
¶
Extra HTTP headers sent on every request (proxy headers, non-Bearer
auth). None sends only the SDK defaults.
extra_body
class-attribute
instance-attribute
¶
Extra request-body fields for vLLM-specific params (guided decoding,
extra sampling). None sends a vanilla body.
create_vllm_provider ¶
Create an OpenAI-compatible AI provider pointed at a local vLLM server.
This is a factory function — no new subclass is needed because vLLM
implements the OpenAI Chat Completions API. The openai SDK is
imported lazily when :class:OpenAIAIProvider is instantiated.
Note
The returned provider inherits :meth:OpenAIAIProvider.available_models,
whose curated catalog lists OpenAI's hosted models — not whatever a
local vLLM server serves. For a vLLM deployment, call
:meth:~OpenAIAIProvider.list_models instead: it queries the server's
/v1/models endpoint and returns the models actually loaded there.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
VLLMConfig
|
vLLM connection settings. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
An |
OpenAIAIProvider
|
class: |
Usage¶
from roomkit.providers.vllm import create_vllm_provider, VLLMConfig
from roomkit.channels.ai import AIChannel
# Configure connection to a local vLLM server
config = VLLMConfig(
model="meta-llama/Llama-3.1-8B-Instruct",
base_url="http://localhost:8000/v1",
)
# Factory returns an OpenAIAIProvider pointed at your vLLM server
provider = create_vllm_provider(config)
# Use with AIChannel like any other AI provider
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[vllm]
Anthropic Provider¶
AnthropicAIProvider ¶
Bases: AIProvider
AI provider using the Anthropic Messages API.
AnthropicConfig ¶
Bases: BaseModel
Anthropic AI provider configuration.
timeout
class-attribute
instance-attribute
¶
Request timeout in seconds (default 60s).
base_url
class-attribute
instance-attribute
¶
Override the base URL (e.g., for Claude Code sandbox proxy).
extra_headers
class-attribute
instance-attribute
¶
Extra headers sent with every request (e.g., X-Tenant-ID).
enable_prompt_caching
class-attribute
instance-attribute
¶
Apply Anthropic prompt caching (explicit cache_control markers) to
the stable request prefix — tools, system prompt, and the conversation
suffix. Every tool-loop round re-sends the full context; without markers
it is billed at the full input rate on every round, with them the prefix
re-reads at the cached rate (10%). Disable for proxies that reject
cache_control blocks.
use_adaptive_thinking
class-attribute
instance-attribute
¶
Send extended thinking as {"type": "adaptive"} instead of the
deprecated {"type": "enabled", "budget_tokens": N}. Anthropic's newer
models (Opus 4.7/4.8, Fable 5) reject budget_tokens with HTTP 400;
adaptive is the modern, recommended shape on Opus 4.6+ and Sonnet 4.6.
Leave False for older models (Sonnet 4.5 and earlier) that only accept
budget_tokens.
supports_custom_temperature
class-attribute
instance-attribute
¶
When False, temperature is omitted from requests. Anthropic's
reasoning models (Opus 4.7/4.8, Fable 5) removed the sampling parameters
and reject temperature with HTTP 400.
Usage¶
from roomkit.providers.anthropic.ai import AnthropicAIProvider
from roomkit.providers.anthropic.config import AnthropicConfig
from roomkit.channels.ai import AIChannel
config = AnthropicConfig(api_key="your-api-key")
provider = AnthropicAIProvider(config)
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[anthropic]
OpenAI Provider¶
OpenAIAIProvider ¶
Bases: AIProvider
AI provider using the OpenAI Chat Completions API.
available_models
classmethod
¶
Curated, offline catalog of OpenAI chat/multimodal models.
list_models
async
¶
List every model id the configured endpoint exposes.
The OpenAI /v1/models response carries only ids — metadata for
known chat models is backfilled from the curated catalog. The raw
list also includes non-chat models (embeddings, audio); they pass
through unfiltered since the endpoint reports no capability field.
generate_structured_stream
async
¶
Yield structured events with <think> tag parsing.
Text inside <think>...</think> is yielded as
:class:StreamThinkingDelta; everything else as
:class:StreamTextDelta. Tool calls are collected from the final
chunks and yielded as :class:StreamToolCall.
OpenAIConfig ¶
Bases: BaseModel
OpenAI AI provider configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
API key for authentication. |
base_url |
str | None
|
Custom base URL for OpenAI-compatible APIs (e.g., Ollama, LM Studio, Azure OpenAI, or other providers). If None, uses the default OpenAI API. |
model |
str
|
Model identifier to use. |
max_tokens |
int
|
Maximum tokens in the response. |
temperature |
float
|
Sampling temperature. |
timeout
class-attribute
instance-attribute
¶
HTTP request timeout in seconds. Override for servers that need longer (e.g. Ollama cold-starting a model on first request).
max_retries
class-attribute
instance-attribute
¶
SDK-level retry count. Default 0 because RoomKit's RetryPolicy handles retries at the right layer with proper backoff and fallback.
include_stream_usage
class-attribute
instance-attribute
¶
When True, request token usage in streaming responses via
stream_options.include_usage. The usage is included in the
final :class:StreamDone event.
use_max_completion_tokens
class-attribute
instance-attribute
¶
Send the output cap as max_completion_tokens instead of the
deprecated max_tokens. OpenAI's newer models (o-series, gpt-5,
gpt-4.1) reject max_tokens outright. Leave False for
OpenAI-compatible servers (vLLM, LM Studio, older Azure deployments)
that only understand max_tokens.
supports_custom_temperature
class-attribute
instance-attribute
¶
When False, temperature is omitted from requests. OpenAI's
reasoning models (o-series, gpt-5) accept only the default
temperature=1 and reject any other value with HTTP 400.
reasoning_effort
class-attribute
instance-attribute
¶
Reasoning depth for OpenAI reasoning models (o-series, gpt-5):
"low" | "medium" | "high". Controls how long the model
reasons (quality vs latency/cost); the reasoning trace itself stays
hidden in the Chat Completions API. None = the model's default.
Only send it for reasoning models — others reject the parameter.
default_headers
class-attribute
instance-attribute
¶
Extra HTTP headers sent on every request, passed to the SDK's
default_headers. Use for an OpenAI-compatible endpoint behind a
reverse proxy that needs custom headers, or a non-Bearer
Authorization scheme (e.g. Basic). None sends only the SDK's
own headers; the api_key Bearer token is unaffected.
extra_body
class-attribute
instance-attribute
¶
Extra JSON fields merged into every Chat Completions request body
via the SDK's extra_body. The route for server-specific params the
OpenAI schema omits — e.g. vLLM guided decoding
(guided_json/guided_choice) and extra sampling (top_k,
repetition_penalty, min_p). None sends a vanilla body.
Usage¶
from roomkit.providers.openai.ai import OpenAIAIProvider
from roomkit.providers.openai.config import OpenAIConfig
from roomkit.channels.ai import AIChannel
config = OpenAIConfig(api_key="your-api-key")
provider = OpenAIAIProvider(config)
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[openai]
Mistral Provider¶
MistralAIProvider ¶
Bases: AIProvider
AI provider using the Mistral AI API.
Supports streaming, tool calling, vision (multimodal models), and
<think> tag parsing for reasoning models.
available_models
classmethod
¶
Curated, offline catalog of Mistral chat/multimodal models.
generate_structured_stream
async
¶
Yield structured events with <think> tag parsing.
Text inside <think>...</think> is yielded as
:class:StreamThinkingDelta; everything else as
:class:StreamTextDelta. Tool calls are accumulated from deltas
and yielded as :class:StreamToolCall.
generate_stream
async
¶
Yield text deltas as they arrive from the Mistral API.
MistralConfig ¶
Bases: BaseModel
Mistral AI provider configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
Mistral API key for authentication. |
model |
str
|
Model identifier (e.g. |
max_tokens |
int
|
Maximum tokens in the response. |
temperature |
float
|
Sampling temperature. |
server_url |
str | None
|
Custom base URL for Mistral-compatible APIs.
If |
reasoning_effort |
str | None
|
Reasoning effort for models that expose it
( |
Usage¶
from roomkit.providers.mistral.ai import MistralAIProvider
from roomkit.providers.mistral.config import MistralConfig
from roomkit.channels.ai import AIChannel
config = MistralConfig(api_key="your-api-key")
provider = MistralAIProvider(config)
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[mistral]
Azure Provider¶
AzureAIProvider ¶
Bases: OpenAIAIProvider
AI provider using Azure AI Studio's OpenAI-compatible Chat Completions API.
Subclasses :class:OpenAIAIProvider — only client initialisation and provider
name differ. All message building, tool handling, response parsing, and
streaming are inherited.
available_models
classmethod
¶
Azure exposes user-named deployments, not a fixed model catalog.
Deployment names are chosen per Azure resource, so there is no
meaningful offline list — use :meth:list_models for the live set.
AzureAIConfig ¶
Bases: BaseModel
Azure AI Studio provider configuration.
Uses the OpenAI-compatible Chat Completions API exposed by Azure AI Foundry deployments (DeepSeek, GPT-4o, Mistral, etc.).
Attributes:
| Name | Type | Description |
|---|---|---|
api_key |
SecretStr
|
Azure API key for authentication. |
azure_endpoint |
str
|
Azure AI Foundry project endpoint URL. |
api_version |
str
|
Azure API version string. |
model |
str
|
Deployment name (no default — user must specify). |
max_tokens |
int
|
Maximum tokens in the response. |
temperature |
float
|
Sampling temperature. |
timeout |
float
|
HTTP request timeout in seconds. |
max_retries
class-attribute
instance-attribute
¶
SDK-level retry count. Default 0 because RoomKit's RetryPolicy handles retries at the right layer with proper backoff and fallback.
include_stream_usage
class-attribute
instance-attribute
¶
When True, request token usage in streaming responses.
use_max_completion_tokens
class-attribute
instance-attribute
¶
Send the output cap as max_completion_tokens rather than the
deprecated max_tokens. Required by newer Azure-hosted OpenAI models;
leave False for deployments that only understand max_tokens.
supports_custom_temperature
class-attribute
instance-attribute
¶
When False, temperature is omitted — reasoning deployments accept
only the default and reject any other value with HTTP 400.
reasoning_effort
class-attribute
instance-attribute
¶
Reasoning depth for reasoning deployments ("low"/"medium"/
"high"); None uses the model default. Only sent for models that
accept it.
extra_body
class-attribute
instance-attribute
¶
Extra JSON fields merged into every request body via the SDK's
extra_body — for deployment-specific params the OpenAI schema omits.
None sends a vanilla body.
Usage¶
from roomkit.providers.azure.ai import AzureAIProvider
from roomkit.providers.azure.config import AzureAIConfig
from roomkit.channels.ai import AIChannel
config = AzureAIConfig(
azure_endpoint="https://your-resource.openai.azure.com/",
api_key="your-api-key",
deployment="your-deployment-name",
)
provider = AzureAIProvider(config)
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[azure]
Ollama Provider (Local / Cloud LLM)¶
Native provider for Ollama. Calls /api/chat directly, so
the think parameter and streamed reasoning work without <think> tag parsing.
See AI Thinking — Native Ollama provider
for thinking, authentication, and sampling options (temperature, num_ctx,
top_p, top_k, min_p, keep_alive).
OllamaAIProvider ¶
Bases: AIProvider
AI provider using Ollama's native API via the ollama-python SDK.
available_models
classmethod
¶
Curated, offline snapshot of popular public Ollama models.
list_models
async
¶
List models installed on the configured Ollama server.
Reads /api/tags for the installed set, then probes /api/show
per model (bounded parallel fan-out) to attach capabilities —
the tags the picker uses to keep completion-only models out of an
embeddings channel and vice-versa. A per-model probe failure yields
no capabilities for that model (older servers don't ship the field);
consumers treat empty as "unknown, allow everywhere".
generate_structured_stream
async
¶
Yield structured events with thinking streamed separately.
Ollama's native streaming emits one chunk per token-ish, each
with message.thinking and/or message.content deltas
plus an optional final message.tool_calls. We pass these
straight through as the corresponding StreamThinkingDelta,
StreamTextDelta, and StreamToolCall events — no tag
parsing, no field reordering.
OllamaConfig ¶
Bases: BaseModel
Ollama AI provider configuration.
Wraps the native Ollama /api/chat endpoint via the
ollama-python SDK. Prefer this over the OpenAI-compatible
shim when the model is a reasoning model (DeepSeek-R1, Qwen 3
thinking variants, etc.) because Ollama exposes the think
parameter and streams the reasoning content as a separate
thinking field — both ignored by the OpenAI-compat endpoint.
Attributes:
| Name | Type | Description |
|---|---|---|
host |
str
|
Base URL of the Ollama server. Default points at the
local daemon. The native API lives under |
model |
str
|
Model identifier to use (e.g. |
max_tokens |
int | None
|
Maximum tokens to generate in the response. Maps
to Ollama's |
temperature |
float
|
Sampling temperature. Maps to
|
timeout |
float
|
HTTP request timeout in seconds. Long default because local models cold-start on first request and reasoning models can take 30-60s before the first token. |
max_retries |
int
|
SDK-level retry count. Default 0 because RoomKit's RetryPolicy handles retries at the right layer with proper backoff and fallback. |
think |
bool | ThinkEffort | None
|
Whether and how hard the model should reason before
answering. |
keep_alive |
str | int | None
|
How long the model stays loaded in memory after
the request. Maps to Ollama's |
num_ctx |
int | None
|
Context window size. Maps to |
top_p |
float | None
|
Nucleus sampling cutoff. Maps to |
top_k |
int | None
|
Top-k sampling cutoff. Maps to |
min_p |
float | None
|
Minimum-probability sampling cutoff. Maps to
|
api_key |
SecretStr | None
|
Bearer token for a protected Ollama endpoint — Ollama
Cloud/Turbo, or a self-hosted server behind a reverse proxy
that checks |
headers |
dict[str, str] | None
|
Extra HTTP headers attached to every request — for a
reverse proxy that needs custom headers, or a non-Bearer
|
Usage¶
from roomkit.providers.ollama import OllamaAIProvider, OllamaConfig
from roomkit.channels.ai import AIChannel
config = OllamaConfig(host="http://localhost:11434", model="llama3.2")
provider = OllamaAIProvider(config)
ai_channel = AIChannel("ai", provider=provider)
Install with: pip install roomkit[ollama]
Streaming¶
StreamEvent
module-attribute
¶
StreamEvent = StreamThinkingDelta | StreamTextDelta | StreamToolCall | StreamDone
StreamTextDelta ¶
Bases: BaseModel
A text delta from a streaming AI response.
StreamThinkingDelta ¶
Bases: BaseModel
A thinking/reasoning delta from a streaming AI response.
Emitted before text deltas when the model is reasoning. A delta may carry
only a signature (with empty thinking): Anthropic streams the
thinking block's opaque signature separately, and it must be preserved so
the block can be echoed back in history without a 400.
StreamToolCall ¶
Bases: BaseModel
A complete tool call extracted from a streaming AI response.
StreamDone ¶
Bases: BaseModel
Signals the end of a streaming AI response.
Response Parts¶
AIThinkingPart ¶
Bases: BaseModel
AI reasoning/thinking block in conversation history.
Used to preserve thinking blocks across tool-loop turns (required by providers like Anthropic that mandate round-trip fidelity).
Attributes:
| Name | Type | Description |
|---|---|---|
thinking |
str
|
The reasoning text produced by the model. |
signature |
str | None
|
Provider-specific opaque token for caching/validation (e.g. Anthropic's thinking block signature). |
AIToolCallPart ¶
Bases: BaseModel
Assistant's tool call in conversation history.
AIToolResultPart ¶
Bases: BaseModel
Tool execution result in conversation history.
result is a plain string for text results, or a list of content parts
(text and/or image) when a tool returns multimodal output — e.g. an edge
tool that returns a screenshot. Providers that support image tool results
(Anthropic) render the parts as content blocks; the rest flatten via
as_text().
as_text ¶
Flatten the result to plain text for providers without image support.
A string result is returned unchanged; a list concatenates its text
parts and substitutes a [image] placeholder for each image part.
split_for_message ¶
Split the result into the tool-message text and its image parts.
Unlike Anthropic — whose Messages API accepts image blocks inside a
tool_result — most providers reject image content in a tool /
function-response message; the image has to ride on a following user
message instead. This returns the text to keep on the tool message
(text parts joined, or a "[see image below]" placeholder when the
result was image-only, so the tool-call/result pairing stays non-empty
and valid) together with the image parts to render natively elsewhere.
A string result yields (result, []) and a text-only list yields
(joined_text, []) — the no-op path that keeps every existing text
tool byte-for-byte unchanged. Only a list carrying an image populates
the second element and triggers a provider's synthetic-image path.
ProviderError ¶
Bases: Exception
Error from an AI provider SDK call.
Attributes:
| Name | Type | Description |
|---|---|---|
retryable |
Whether the caller should retry the request. |
|
provider |
Name of the provider that raised the error. |
|
status_code |
HTTP status code from the provider, if available. |