LastChat Catalog — Authoring Guide
The catalog (catalog/lastchat_catalog.json) is the source-of-truth for model metadata,
provider presets, and service provider registries. It is a layered inheritance system.
Always express metadata through the highest applicable layer — avoid repeating fields that
a parent layer already provides.
1. Top-Level Sections
| JSON key | Kotlin type | Purpose |
|---|---|---|
providers |
List<CatalogProvider> |
Preset AI provider configs (base URL, auth type, icon, setup defaults) |
model_families |
List<CatalogModelFamily> |
Pattern-based metadata inherited by all matching model IDs |
global_rules |
List<CatalogModelRule> |
Cross-family rules evaluated before family matching |
model_overrides |
List<CatalogModelOverride> |
Exact-ID entries; primarily bind model IDs to provider UUIDs |
search_providers |
List<CatalogServiceProvider> |
Web search service registry |
tts_providers |
List<CatalogTTSProvider> |
Text-to-speech provider registry |
stt_providers |
List<CatalogServiceProvider> |
Speech-to-text provider registry |
Kotlin data models live in:
app/src/main/java/me/rerere/rikkahub/data/ai/models/ModelCatalog.kt
2. Resolution Pipeline
For every model ID the app encounters, metadata is built up in this exact order. Each layer overrides the previous:
1. global_rules — pattern rules across all families (e.g. "embed" → EMBEDDING type)
2. model_families — family defaults: type, icon, modalities, abilities, provider_slug
3. └─ versions — sub-patterns within a family that override specific fields
4. model_overrides — exact ID + provider_id match; sets provider_ids and narrow corrections
The builder starts empty and each matched layer is applied with applyRule → applyFamily
→ applyVersion → applyOverride. A model is only resolved if at least one rule matched
(hasMatchedRule = true).
Critical: Icon Resolution
CatalogModelOverride has no icon field. Icons are always inherited from the matched
model_families entry (set via builder.iconUrl = matchedFamily.icon?.toCatalogIconUrl()).
Never put an icon in a model_overrides entry — it will be silently ignored.
3. ModelType Values
| Value | Meaning | Auto input modality | Auto output modality |
|---|---|---|---|
CHAT |
Conversational LLM | ["TEXT"] |
["TEXT"] |
EMBEDDING |
Vector embedding model | ["TEXT"] |
["TEXT"] |
IMAGE |
Image generation | ["TEXT"] |
["IMAGE"] |
STT |
Speech-to-text transcription | ["AUDIO"] |
["TEXT"] |
toModelTypeOrNull() in ModelMetadataResolver.kt maps these strings:
| String | → ModelType |
|---|---|
"chat" |
CHAT |
"embedding" |
EMBEDDING |
"image", "image_generation" |
IMAGE |
"stt" |
STT |
4. Modality Rules
"AUDIO"must only appear ininput_modalitiesofModelType.STTentries.- Chat models must never have
"AUDIO"in theirinput_modalities— there is no audio-to-chat pipeline in the app. - The UI modality selector only exposes
TEXTandIMAGEfor user-editable chat models. "AUDIO"inoutput_modalitiesis not used by any current feature.
5. Layer-by-Layer Authoring Guide
5.1 model_families — Define a family when models share a brand/architecture name
Required fields: id, match_patterns, icon, type
Optional: input_modalities, output_modalities, abilities, provider_slug, versions
{
"id": "whisper",
"aliases": ["whisper"],
"match_patterns": ["whisper", "distil-whisper"],
"icon": "icons/openai.svg",
"type": "STT",
"input_modalities": ["AUDIO"],
"output_modalities": ["TEXT"],
"abilities": [],
"provider_slug": "openai",
"versions": []
}
match_patterns are regex patterns tested against the model ID and its canonical form.
Use anchors like (^|[/._-]) and (?=$|[:/._-]) to avoid false positives.
Available icons: All SVG files in catalog/icons/. Common ones:
openai.svg, gemini.svg, claude.svg, groq.svg, deepseek.svg, mistral.svg,
meta.svg, qwen.svg, siliconflow.svg, dashscope.svg, deepinfra.svg,
fireworks.svg, together.svg, volcengine.svg, minimax.svg, zhipu.svg,
aihubmix.svg, novita.svg, gemma.svg, internlm.svg, baidu.svg, spark.svg
5.2 versions (inside a family) — Override specific fields for model sub-variants
Use a version when a subset of models within a family differs in type, input_modalities,
output_modalities, abilities, or reasoning_behavior.
Fields are nullable — only set what differs from the family default:
{
"id": "gpt-transcribe",
"match_patterns": ["gpt-.*-transcribe"],
"type": "STT",
"input_modalities": ["AUDIO"],
"output_modalities": ["TEXT"],
"abilities": []
}
{
"id": "gemini-image",
"match_patterns": ["gemini.*image"],
"type": "IMAGE",
"image_generation_method": "multimodal",
"input_modalities": ["TEXT", "IMAGE"],
"output_modalities": ["IMAGE"],
"abilities": []
}
Versions are matched in order — first match wins within a family.
5.3 model_overrides — Bind model IDs to provider UUIDs (and nothing else if possible)
The minimal correct form is only three fields:
{
"id": "whisper-large-v3",
"canonical_model_id": "whisper-large-v3",
"provider_ids": ["f9fe0a18-2b30-46e1-9c57-5562654e8d64"]
}
Only add extra fields when the model genuinely needs to differ from its matched family:
{
"id": "dall-e-3",
"canonical_model_id": "dall-e-3",
"provider_ids": ["8f9d0c75-8f29-4a27-9c2b-f8d4fd5f3e91"],
"type": "IMAGE",
"image_generation_method": "diffusion",
"input_modalities": ["TEXT"],
"output_modalities": ["IMAGE"]
}
5.4 reasoning_config — Declare model reasoning mode & UI behavior
Can be set on model_families, versions, or model_overrides:
{
"reasoning_config": {
"type": "effort",
"supported_levels": ["off", "auto", "low", "medium", "high", "max"]
}
}
"binary": Models with a simple on/off thinking toggle (e.g. DeepSeek-R1, Grok, Doubao, InternLM). UI renders a clean 2-item toggle (Disabled vs Enabled)."effort": Models with qualitative depth scales (e.g. OpenAI o1/o3/o4/gpt-5.6, Gemini 3). UI displays qualitative options without misleading token numbers."budget": Models taking token budgets (e.g. Claude 3.7+, Gemini 2.5, DashScope Qwen). Supportsmin_tokens,max_tokens, andpreset_tokens.
5.5 context_window and max_images_in_context
Both fields are optional, but strongly recommended for completeness. They can be set at any layer — family, version, or override.
{
"context_window": 1000000,
"max_images_in_context": 100
}
context_window (integer, in tokens): The maximum input context the model supports.
Common correct values by family (as of 2026):
| Family | context_window |
|---|---|
| GPT-5.6 Sol/Terra | 1 050 000 |
| GPT-5.6 Luna | 512 000 |
| GPT-4o, GPT-4o-mini | 128 000 |
| o1, o3, o4-mini | 200 000 |
| Claude 5 (all) | 1 000 000 |
| Claude 3.7/3.5 Sonnet | 1 000 000 (beta) |
| Claude 3.5 Haiku | 200 000 |
| Gemini 1.5 Pro | 2 000 000 |
| Gemini 2.0/2.5/3.0 | 1 000 000 |
| Gemma 4 (31B, 26B A4B) | 256 000 |
| Gemma 4 (E4B, E2B) | 128 000 |
| Gemma 3 (27B, 12B, 4B) | 128 000 |
| Gemma 2 (27B, 9B, 2B) | 8 192 |
| Llama 4 Scout | 10 000 000 |
| Llama 4 Maverick | 1 000 000 |
| DeepSeek-V4 Pro/Flash | 1 000 000 (Max output 384k) |
| DeepSeek-V4 Vision Exp | 1 000 000 (Vision: TEXT+IMAGE) |
| DeepSeek-VL2 | 128 000 (Vision: TEXT+IMAGE) |
| DeepSeek-V3, R1 | 128 000 |
| Mistral Large 2 | 128 000 |
| Mistral Small 4 | 256 000 |
| Pixtral 12B / Large | 128 000 |
| Qwen 3 (3.8-Max, 3.5, 3-VL) | 1 000 000 (Vision: TEXT+IMAGE) |
| Qwen-Plus, Qwen-Turbo, Qwen-Long | 1 000 000 |
| Qwen-Max | 262 144 |
| Qwen 2.5-VL, QvQ-72B | 131 072 (Vision: TEXT+IMAGE) |
| QwQ-32B, Qwen 2.5 | 131 072 |
| Grok 3/3-mini | 131 072 |
| Grok 4 | 256 000 |
| MiniMax-M3 | 1 000 000 |
| MiniMax-M2.x | 204 800 |
| Kimi K3 | 1 000 000 |
| Kimi K2.5 | 256 000 |
| Moonshot V1 | 128 000 |
| Hunyuan-T1, TurboS | 256 000 |
| StepFun 3.5/3.7 Flash | 256 000 |
| GLM-5, 5.2 | 1 000 000 |
| GLM-5V-Turbo, 5.3 Flash | 200 000 |
max_images_in_context (integer): Maximum images the model can accept in a single request.
Only set when the model actually accepts image input. Omit for text-only and embedding models.
| Family | max_images_in_context |
|---|---|
| Gemini (all vision) | 3 600 |
| Claude (all) | 100 |
| GPT-5.6 Sol/Terra, o3, o4-mini | 50 |
| GPT-4o | 50 |
| Gemma 4 / Gemma 3 (vision) | 10 |
| Llama 4 (multimodal) | 10 |
| Pixtral Large | 30 |
| Pixtral 12B | 30 |
| MiniMax-M3, M2 Vision | 10 |
| Kimi K3, K2.5 | 10 |
| GLM-5V, 5.3 Flash | 10 |
| Qwen VL / 3.8 (vision) | 50 |
Tip: When in doubt about the exact limit, use a conservative lower bound rather than an inflated number.
max_images_in_contextis a UX hint, not enforced at the API call layer.
api_aliases can list alternate API IDs that map to the same canonical model:
{
"id": "gemini-2.5-flash-image",
"canonical_model_id": "gemini-2.5-flash-image",
"api_aliases": ["models/gemini-2.5-flash-image", "nano-banana"],
"provider_ids": ["4e28ef61-8b96-4a1e-9f91-15b32ce6d886"]
}
5.4 global_rules — Cross-family rules applied to every model ID
Useful for patterns that span families, e.g. auto-classifying any model ID containing
embed as an embedding model:
{
"id": "global-embedding",
"match_patterns": ["embed"],
"type": "EMBEDDING",
"input_modalities": ["TEXT"],
"output_modalities": ["TEXT"],
"abilities": []
}
5.5 providers — Preset AI provider configs
{
"id": "f9fe0a18-2b30-46e1-9c57-5562654e8d64",
"name": "Groq",
"description": "Ultra-fast inference for open models.",
"type": "openai",
"base_url": "https://api.groq.com/openai/v1",
"icon": "icons/groq.svg",
"preset": true
}
Provider type is one of: "openai", "google", "claude".
Optional fields: chat_completions_path, use_response_api, stream_options_mode,
image_response_modalities_mode, reasoning_content_replay_mode, balance_option,
signup_url, api_key_url, setup_recommended, setup_models, setup_defaults.
5.6 stt_providers — Speech-to-text provider registry
These are displayed in the STT provider setup screen. They are not the same as
providers (which are full AI provider presets with UUIDs):
{
"id": "groq",
"name": "Groq",
"icon": "icons/groq.svg"
}
The id here is a slug used to match providers by name — not a UUID.
6. Key Provider UUIDs Reference
Always use these UUIDs in provider_ids arrays:
| UUID | Provider |
|---|---|
8f9d0c75-8f29-4a27-9c2b-f8d4fd5f3e91 |
OpenAI |
4e28ef61-8b96-4a1e-9f91-15b32ce6d886 |
Google AI Studio |
f9fe0a18-2b30-46e1-9c57-5562654e8d64 |
Groq |
448c27de-c5e0-4434-8efa-932d0613dd9b |
DeepSeek |
5a4e6046-8309-40a0-a294-2e621e876f4d |
Mistral |
c7e2b7da-a579-4a94-9b24-9b24bfa1e9ad |
DeepInfra |
8e9f2910-c114-411a-b302-18cb92193b2a |
Fireworks AI |
b38e1192-5b47-4d2a-a163-f622a2eaf717 |
SiliconFlow |
c5e85cbb-8e28-454c-96d0-0585750d081c |
AiHubMix |
5e729975-9794-4013-af7e-63a548cf3356 |
Alibaba Cloud (DashScope / Qwen) |
d2eb13cb-8c0a-4885-9e74-f4b56c8d5b24 |
Volcengine Ark |
bf741ca1-57d6-4444-93ff-18305c43d9b4 |
Together AI |
d5734028-d39b-4d41-9841-fd648d65440e |
OpenRouter |
1fd6005f-a4a7-4b2e-81b2-2d4fa97d5123 |
Anthropic Claude |
5d8c3e12-b147-4977-bc5b-426b68da401f |
GitHub Models |
11c4728d-d349-4328-8d07-28d5d4d5e901 |
Cloudflare Workers AI |
f8e32910-c114-411a-b302-18cb92193b3b |
Replicate |
fb74da0c-cf14-45ff-98de-1e3df6a94b3c |
Cerebras |
7a9b0c10-d8f9-467f-94d0-258fe3da49b4 |
SambaNova |
ce456892-f6bb-4598-bbb5-2658fe0d2955 |
InternLM |
To find a UUID for a provider not listed above, search for "name": "<ProviderName>" in
catalog/lastchat_catalog.json.
7. Existing STT Model Families
These families are already defined and auto-classify matching model IDs as STT:
| Family ID | match_patterns |
Icon |
|---|---|---|
whisper |
["whisper", "distil-whisper"] |
openai.svg |
sensevoice |
["sensevoice", "FunAudioLLM"] |
siliconflow.svg |
paraformer |
["paraformer"] |
dashscope.svg |
gpt → gpt-transcribe version |
["gpt-.*-transcribe"] |
(inherits openai.svg from gpt family) |
If a new STT model's name contains whisper, distil-whisper, sensevoice,
FunAudioLLM, or paraformer, no new family is needed — add a minimal override only.
8. Anti-Patterns ❌
| Wrong | Correct |
|---|---|
Adding type/icon/modalities to an override when a family already defines them |
Minimal override: id + canonical_model_id + provider_ids only |
Putting "icon" in a model_overrides entry |
Icons come from the family — CatalogModelOverride has no icon field |
"AUDIO" in input_modalities of a CHAT model |
Only STT-type models should have "AUDIO" input |
| Creating a new family for a single model with no naming siblings | Use a lean override, or a versions entry in the closest existing family |
Leaving out canonical_model_id in an override |
Always set it — it's used for deduplication and alias resolution |
9. Step-by-Step Checklists
Adding a new STT model
- Check if a family already matches the model ID:
whisper*,distil-whisper*→whisperfamily ✓gpt-*-transcribe→gptfamily +gpt-transcribeversion ✓sensevoice*,FunAudioLLM/*→sensevoicefamily ✓paraformer*→paraformerfamily ✓
- If matched: add only
{ "id", "canonical_model_id", "provider_ids" }tomodel_overrides. - If not matched: create a new
model_familiesentry (type STT, icon, match_patterns), then step 2. - Never add
"AUDIO"to any chat model.
Adding a new chat model family
- Add to
model_families:id,match_patterns,icon,type: "CHAT",input_modalities: ["TEXT"]. - Add
"IMAGE"toinput_modalitiesonly if the family is genuinely vision-capable. - Add
versionsfor meaningful sub-variants (vision, reasoning, embedding, image-gen, STT). - Add
model_overridesentries only for models that needprovider_idsbinding. - Update
updated_atdate at the top of the catalog file.
Adding a new provider preset
- Generate or reuse a UUID for the
idfield. - Add to
providersarray withname,description,type,base_url,icon,preset: true. - Add the UUID to relevant
model_overridesentries'provider_idsarrays. - If the provider supports STT, add a corresponding entry to
stt_providers(slug-based, no UUID).