Separate online and airplane AI profiles.
This commit is contained in:
@@ -12,8 +12,12 @@ use "./media.allium" as media
|
||||
entity AiEndpoint {
|
||||
kind: online | airplane
|
||||
url: String
|
||||
api_key: String? -- encrypted via SecureKeyStore; null for local models
|
||||
model: String
|
||||
api_key: String? -- encrypted via SecureKeyStore; required online, optional locally
|
||||
model: String -- chat model
|
||||
title_model: String?
|
||||
image_model: String?
|
||||
chat_supports_tools: Boolean? -- operator override of discovered capability
|
||||
image_supports_vision: Boolean? -- operator override of discovered capability
|
||||
-- online: cloud provider (OpenAI, Anthropic-via-proxy, etc.)
|
||||
-- airplane: local model (Ollama, LM Studio, etc.)
|
||||
}
|
||||
@@ -81,6 +85,10 @@ surface AiEndpointSurface {
|
||||
endpoint.url
|
||||
endpoint.api_key when endpoint.api_key != null
|
||||
endpoint.model
|
||||
endpoint.title_model when endpoint.title_model != null
|
||||
endpoint.image_model when endpoint.image_model != null
|
||||
endpoint.chat_supports_tools when endpoint.chat_supports_tools != null
|
||||
endpoint.image_supports_vision when endpoint.image_supports_vision != null
|
||||
}
|
||||
|
||||
surface AiModelSurface {
|
||||
@@ -177,8 +185,10 @@ surface AiConfigurationSurface {
|
||||
facing _: AiOperator
|
||||
|
||||
provides:
|
||||
SetAiEndpointRequested(kind, url, api_key, model)
|
||||
SetAiEndpointRequested(kind, url, api_key, model, title_model, image_model, chat_supports_tools, image_supports_vision)
|
||||
RemoveAiEndpointRequested(kind)
|
||||
RefreshEndpointModelsRequested(kind, url, api_key)
|
||||
TestEndpointModelsRequested(kind)
|
||||
RefreshModelCatalogRequested(source)
|
||||
}
|
||||
|
||||
@@ -221,13 +231,17 @@ config {
|
||||
}
|
||||
|
||||
rule SetAiEndpoint {
|
||||
when: SetAiEndpointRequested(kind, url, api_key, model)
|
||||
when: SetAiEndpointRequested(kind, url, api_key, model, title_model, image_model, chat_supports_tools, image_supports_vision)
|
||||
ensures:
|
||||
let endpoint = AiEndpoint.created(
|
||||
kind: kind,
|
||||
url: url,
|
||||
api_key: api_key,
|
||||
model: model
|
||||
model: model,
|
||||
title_model: title_model,
|
||||
image_model: image_model,
|
||||
chat_supports_tools: chat_supports_tools,
|
||||
image_supports_vision: image_supports_vision
|
||||
)
|
||||
endpoint.kind = kind
|
||||
}
|
||||
@@ -335,6 +349,22 @@ rule RefreshModelCatalog {
|
||||
ensures: ModelCatalogUpdated()
|
||||
}
|
||||
|
||||
rule RefreshEndpointModels {
|
||||
when: RefreshEndpointModelsRequested(kind, url, api_key)
|
||||
requires: url != ""
|
||||
requires: kind = airplane or api_key != null
|
||||
-- Discovery calls GET /models and does not require a selected model.
|
||||
ensures: EndpointModelsLoaded(kind)
|
||||
}
|
||||
|
||||
rule TestEndpointModels {
|
||||
when: TestEndpointModelsRequested(kind)
|
||||
requires: exists endpoint in AiEndpoints where endpoint.kind = kind
|
||||
-- Sends a minimal chat-completions request to each distinct configured
|
||||
-- chat, title, and image model for this endpoint.
|
||||
ensures: EndpointModelsResponded(kind)
|
||||
}
|
||||
|
||||
invariant AirplaneModeGating {
|
||||
-- Endpoint routing based on airplane (offline) mode:
|
||||
-- airplane_mode = true -> use airplane endpoint (local model)
|
||||
@@ -347,8 +377,8 @@ invariant AirplaneModeGating {
|
||||
|
||||
invariant AirplaneModeModelSwap {
|
||||
-- In airplane mode, cloud models are never contacted.
|
||||
-- Chat uses the configured offline chat model when needed.
|
||||
-- Image analysis uses the configured offline vision-capable model when needed.
|
||||
-- Chat, title generation, and image analysis use the models and capability
|
||||
-- overrides configured for the active endpoint profile.
|
||||
-- If no suitable offline model is configured, the operation fails with
|
||||
-- actionable guidance instead of silently falling back to the online endpoint.
|
||||
}
|
||||
@@ -356,9 +386,11 @@ invariant AirplaneModeModelSwap {
|
||||
invariant TwoEndpointModel {
|
||||
-- Two configurable OpenAI-compatible endpoints:
|
||||
-- online: for cloud providers (requires API key)
|
||||
-- airplane: for local models (no API key required)
|
||||
-- airplane: for local models (API key optional)
|
||||
-- Both use the OpenAI Chat Completions wire format.
|
||||
-- Endpoint selection is configurable rather than tied to hard-coded providers.
|
||||
-- URL, credentials, chat/title/image models, and capability overrides are
|
||||
-- independent. Endpoint selection is controlled only by the status-bar
|
||||
-- airplane-mode switch, not by a duplicate Settings toggle.
|
||||
}
|
||||
|
||||
invariant AdvisoryModelCatalog {
|
||||
@@ -387,6 +419,12 @@ invariant VisionCapabilityGate {
|
||||
-- before the runtime sends multimodal requests to them.
|
||||
}
|
||||
|
||||
invariant OperatorCapabilityOverrides {
|
||||
-- Discovered tool and vision capabilities prefill the profile controls.
|
||||
-- Explicit operator overrides take precedence at runtime so models with
|
||||
-- incomplete metadata can enable or disable tools and image input safely.
|
||||
}
|
||||
|
||||
invariant ChatContextTruncation {
|
||||
-- Chat requests are trimmed to fit within the selected model's context window.
|
||||
-- Oldest user/assistant pairs are dropped first.
|
||||
|
||||
@@ -53,17 +53,21 @@ value SettingsCategoryRow {
|
||||
}
|
||||
|
||||
value SettingsAISection {
|
||||
anthropic_api_key: String? -- masked + Change/Save
|
||||
mistral_api_key: String? -- masked + Change/Save
|
||||
ollama_enabled: Boolean -- toggle, shows model capabilities table (tools/vision)
|
||||
lm_studio_enabled: Boolean -- toggle, shows model capabilities table
|
||||
offline_mode: Boolean -- toggle, switches between online and airplane endpoint
|
||||
default_model: String? -- select from active endpoint models
|
||||
title_model: String? -- select
|
||||
image_analysis_model: String? -- select (vision-capable only)
|
||||
online: SettingsAIEndpoint
|
||||
airplane: SettingsAIEndpoint
|
||||
system_prompt: String -- textarea (12 rows) + Save + Reset to Default
|
||||
}
|
||||
|
||||
value SettingsAIEndpoint {
|
||||
url: String
|
||||
api_key: String? -- masked; optional for airplane endpoint
|
||||
chat_model: String?
|
||||
title_model: String?
|
||||
image_model: String?
|
||||
chat_supports_tools: Boolean
|
||||
image_supports_vision: Boolean
|
||||
}
|
||||
|
||||
value SettingsTechnologySection {
|
||||
semantic_similarity_enabled: Boolean -- checkbox: enable duplicate search + related-post embeddings
|
||||
-- Scripting runtime is fixed at the application layer; no runtime switch is exposed.
|
||||
@@ -130,8 +134,9 @@ surface SettingsViewSurface {
|
||||
SettingsCategoryUpdated(category_row)
|
||||
SettingsCategoryRemoved(category_name)
|
||||
SettingsCategoriesResetToDefaults()
|
||||
SettingsAIApiKeySaved(provider, key)
|
||||
SettingsAIModelRefreshRequested(endpoint)
|
||||
SettingsAIProfileSaved(kind, endpoint)
|
||||
SettingsAIModelRefreshRequested(kind, url, api_key)
|
||||
SettingsAIChatTestRequested(kind)
|
||||
SettingsAISystemPromptSaved(prompt)
|
||||
SettingsAISystemPromptReset()
|
||||
SettingsPublishingSaved(publishing_data)
|
||||
@@ -179,23 +184,27 @@ surface SettingsViewSurface {
|
||||
-- "Reset to Defaults" restores default categories set.
|
||||
|
||||
@guarantee AISection
|
||||
-- Section 4: Anthropic API key, Mistral API key (both masked + Change/Save),
|
||||
-- Ollama toggle (with model capabilities table: tools/vision),
|
||||
-- LM Studio toggle (with capabilities table),
|
||||
-- Offline mode toggle (with dedicated model selectors for chat/title/image),
|
||||
-- Default model (with catalog info: max output, context window),
|
||||
-- Title model, Image analysis model,
|
||||
-- Section 4: independent Online and Airplane endpoint cards. Each has
|
||||
-- URL, masked API key (optional locally), Refresh Models, chat/title/image
|
||||
-- selectors, explicit tools/vision capability overrides, and Test Chat.
|
||||
-- Settings has no airplane-mode toggle; the status-bar switch selects
|
||||
-- the active profile and immediately swaps chat/model routing.
|
||||
-- System prompt (textarea config.settings_system_prompt_rows rows + Save + Reset).
|
||||
|
||||
@guarantee AIApiKeyValidation
|
||||
-- On Save: test API call to endpoint before persisting.
|
||||
-- On failure: toast error message, key not saved.
|
||||
-- On success: key encrypted via SecureKeyStore, masked display shown.
|
||||
-- API keys are encrypted via SecureKeyStore and displayed masked.
|
||||
-- Online profiles require a key; airplane profiles permit authenticated
|
||||
-- or unauthenticated local OpenAI-compatible endpoints.
|
||||
|
||||
@guarantee AIModelRefresh
|
||||
-- Refresh Models button per endpoint fetches model list from URL.
|
||||
-- Model selector populated from fetched list.
|
||||
-- Per-model info: max output tokens, context window (when available).
|
||||
-- Discovery requires URL and online credentials but no preselected model.
|
||||
-- All three role selectors are populated independently from the result;
|
||||
-- advertised capability metadata prefills tools and vision overrides.
|
||||
|
||||
@guarantee AIChatTest
|
||||
-- Test Chat sends a minimal request through every distinct model selected
|
||||
-- for chat, title generation, or image analysis in the chosen profile.
|
||||
|
||||
@guarantee TechnologySection
|
||||
-- Section 5: Semantic Similarity toggle
|
||||
|
||||
Reference in New Issue
Block a user