142 lines
4.6 KiB
Plaintext
142 lines
4.6 KiB
Plaintext
-- allium: 1
|
|
-- bDS AI Integration
|
|
-- Scope: core (one-shot operations), extension Bucket C (chat + streaming)
|
|
-- Distilled from: src/main/engine/ChatEngine.ts, ai/providers.ts,
|
|
-- ai/chat.ts, ai/tasks.ts, SecureKeyStore.ts
|
|
-- Note: Rust rewrite simplifies providers to two configurable
|
|
-- OpenAI-compatible endpoints (online + airplane mode),
|
|
-- replacing the TypeScript app's 4 named providers.
|
|
|
|
use "./post.allium" as post
|
|
use "./media.allium" as media
|
|
|
|
entity AiEndpoint {
|
|
kind: online | airplane
|
|
url: String
|
|
api_key: String? -- encrypted via SecureKeyStore; null for local models
|
|
model: String
|
|
-- online: cloud provider (OpenAI, Anthropic-via-proxy, etc.)
|
|
-- airplane: local model (Ollama, LM Studio, etc.)
|
|
}
|
|
|
|
entity SecureKeyStore {
|
|
-- Encrypts API keys using OS keychain
|
|
-- macOS: Keychain, Windows: DPAPI, Linux: libsecret
|
|
-- Stored as base64 in settings table with __encrypted_ prefix
|
|
-- No plain-text fallback
|
|
}
|
|
|
|
entity ChatConversation {
|
|
title: String
|
|
model: String
|
|
created_at: Timestamp
|
|
updated_at: Timestamp
|
|
|
|
messages: ChatMessage with conversation = this
|
|
}
|
|
|
|
entity ChatMessage {
|
|
conversation: ChatConversation
|
|
role: system | user | assistant | tool
|
|
content: String
|
|
token_usage_input: Integer?
|
|
token_usage_output: Integer?
|
|
created_at: Timestamp
|
|
}
|
|
|
|
-- One-shot AI tasks (core scope, no streaming)
|
|
-- All use OpenAI Chat Completions wire format.
|
|
-- Endpoint routing: see AirplaneModeGating invariant below.
|
|
-- When no endpoint configured for current mode: disable AI, show toast.
|
|
|
|
rule AnalyzeTaxonomy {
|
|
when: AnalyzeTaxonomyRequested(post)
|
|
requires: active_endpoint_configured
|
|
-- Suggests tags and categories for a post
|
|
ensures: TaxonomySuggestion(tags, categories)
|
|
}
|
|
|
|
rule AnalyzeImage {
|
|
when: AnalyzeImageRequested(media)
|
|
requires: active_endpoint_configured
|
|
requires: is_image(media.mime_type)
|
|
-- Vision model generates alt text and caption
|
|
ensures: ImageAnalysisResult(alt, caption)
|
|
}
|
|
|
|
rule AnalyzePost {
|
|
when: AnalyzePostRequested(post)
|
|
requires: active_endpoint_configured
|
|
-- Generates title, excerpt, slug suggestions
|
|
ensures: PostAnalysisResult(title, excerpt, slug)
|
|
}
|
|
|
|
rule DetectLanguage {
|
|
when: DetectLanguageRequested(text)
|
|
requires: active_endpoint_configured
|
|
ensures: LanguageDetectionResult(language_code)
|
|
}
|
|
|
|
rule TranslatePost {
|
|
when: TranslatePostRequested(post, target_language)
|
|
requires: active_endpoint_configured
|
|
-- Translates title, excerpt, content to target language
|
|
ensures: TranslationResult(title, excerpt, content)
|
|
}
|
|
|
|
rule TranslateMedia {
|
|
when: TranslateMediaRequested(media, target_language)
|
|
requires: active_endpoint_configured
|
|
-- Translates title, alt, caption to target language
|
|
ensures: MediaTranslationResult(title, alt, caption)
|
|
}
|
|
|
|
-- Chat (extension Bucket C scope, with streaming and tool use)
|
|
|
|
rule StartChat {
|
|
when: StartChatRequested(model)
|
|
ensures: ChatConversation.created(model: model)
|
|
}
|
|
|
|
rule SendChatMessage {
|
|
when: SendChatMessageRequested(conversation, content)
|
|
requires: active_endpoint_configured
|
|
ensures: ChatMessage.created(conversation: conversation, role: user, content: content)
|
|
ensures: AiStreamingResponse(conversation)
|
|
-- AI SDK v6 streamText() with tool-call loop (max 10 rounds)
|
|
-- Blog data tools for post/media querying during chat
|
|
-- Token usage tracking (input, output, cache read/write)
|
|
}
|
|
|
|
-- Model catalog
|
|
|
|
rule RefreshModelCatalog {
|
|
when: RefreshModelCatalogRequested(endpoint)
|
|
-- Queries the endpoint's model list API
|
|
-- 5-minute cache TTL
|
|
ensures: ModelCatalogUpdated(endpoint)
|
|
}
|
|
|
|
invariant AirplaneModeGating {
|
|
-- Endpoint routing based on airplane (offline) mode:
|
|
-- airplane_mode = true -> use airplane endpoint (local model)
|
|
-- airplane_mode = false -> use online endpoint (cloud provider)
|
|
-- active_endpoint_configured = true iff the endpoint for the
|
|
-- current mode has a non-empty url (and api_key for online).
|
|
-- When active endpoint is not configured: AI is unavailable,
|
|
-- show toast "AI unavailable — configure {online|airplane} endpoint in Settings"
|
|
}
|
|
|
|
invariant TwoEndpointModel {
|
|
-- Two configurable OpenAI-compatible endpoints:
|
|
-- online: for cloud providers (requires API key)
|
|
-- airplane: for local models (no API key required)
|
|
-- Both use the OpenAI Chat Completions wire format.
|
|
-- This replaces the TypeScript app's 4 named provider model.
|
|
}
|
|
|
|
invariant SecureKeyStorage {
|
|
-- API keys are never stored in plain text
|
|
-- Always encrypted via OS keychain before DB storage
|
|
}
|