mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-22 16:02:38 +00:00
* feat: add instance-level AI settings with workspace fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add AI step to onboarding setup wizard Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: thread workspace prop through resource editor and disable chat offset Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Revert "fix: thread workspace prop through resource editor and disable chat offset" This reverts commit 9fea9cc0c239f6432d1fef1487c45e74ab752e21. * fix: set workspace store and disable chat offset during AI setup step Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: thread workspace and disableChatOffset props through resource editors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: populate workspace and user stores for AI step path component Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: initialize AI clients for test key during onboarding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract AI config state into InstanceAISettings component Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: move AI config state ownership into AISettings component Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Persist instance AI settings before navigation * Reload effective workspace AI state after save * Scope AI key tests to the rendered workspace * Add post-create AI onboarding for new workspaces * Unify instance AI settings header * Fix instance AI drawer offset on workspace selection * Add instance AI fallback settings behavior * Update sqlx metadata * Update sqlx metadata * Clarify active instance AI in workspace settings * Refresh workspace AI state after instance AI save * Declare instance AI summary in API schema * Normalize empty instance AI config handling * Clean up workspace AI settings UI * Unify AI config provider checks * Split AI settings metadata from effective config * Propagate instance AI cache invalidation across servers * Fix AI settings dirty state tracking * Update sqlx metadata --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
4.1 KiB
Svelte
150 lines
4.1 KiB
Svelte
<script lang="ts">
|
|
import { JobService, SettingService, WorkspaceService, type AIConfig } from '$lib/gen'
|
|
import { setCopilotInfo } from '$lib/aiStore'
|
|
import { workspaceStore, userStore } from '$lib/stores'
|
|
import { getUserExt } from '$lib/user'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { workspaceAIClients } from '../copilot/lib'
|
|
import AISettings from '../workspaceSettings/AISettings.svelte'
|
|
import { Alert, Button } from '../common'
|
|
|
|
interface Props {
|
|
hasUnsavedChanges?: boolean
|
|
disableChatOffset?: boolean
|
|
showHubSync?: boolean
|
|
}
|
|
|
|
let {
|
|
hasUnsavedChanges = $bindable(false),
|
|
disableChatOffset = false,
|
|
showHubSync = false
|
|
}: Props = $props()
|
|
|
|
let initialConfig: AIConfig | undefined = $state(undefined)
|
|
let loaded = $state(false)
|
|
let aiSettings: AISettings | undefined = $state(undefined)
|
|
|
|
async function loadConfig() {
|
|
try {
|
|
initialConfig =
|
|
((await SettingService.getGlobal({ key: 'ai_config' })) as AIConfig | undefined) ?? {}
|
|
loaded = true
|
|
} catch (e) {
|
|
console.error('Failed to load instance AI config', e)
|
|
sendUserToast('Failed to load instance AI config', true)
|
|
}
|
|
}
|
|
|
|
async function handleCustomSave(config: AIConfig) {
|
|
const hasProviders = Object.keys(config.providers ?? {}).length > 0
|
|
await SettingService.setGlobal({
|
|
key: 'ai_config',
|
|
requestBody: { value: hasProviders ? config : null }
|
|
})
|
|
if ($workspaceStore) {
|
|
try {
|
|
const effectiveConfig = await WorkspaceService.getCopilotInfo({
|
|
workspace: $workspaceStore
|
|
})
|
|
setCopilotInfo(effectiveConfig)
|
|
} catch (e) {
|
|
console.error('Failed to refresh workspace AI state after instance save', e)
|
|
}
|
|
}
|
|
sendUserToast('Instance AI settings saved')
|
|
}
|
|
|
|
export async function persistBeforeExit(): Promise<boolean> {
|
|
return (await aiSettings?.saveIfDirtyAndValid()) ?? true
|
|
}
|
|
|
|
// Ensure stores are set (this page may bypass the (logged) layout)
|
|
async function ensureStores() {
|
|
if (!$workspaceStore) {
|
|
$workspaceStore = 'admins'
|
|
}
|
|
if (!$userStore) {
|
|
$userStore = await getUserExt($workspaceStore)
|
|
}
|
|
workspaceAIClients.init($workspaceStore)
|
|
}
|
|
|
|
ensureStores()
|
|
loadConfig()
|
|
|
|
// --- Hub sync ---
|
|
let hubSyncStatus: 'idle' | 'loading' | 'success' | 'error' = $state('idle')
|
|
let hubSyncMessage = $state('')
|
|
|
|
async function syncFromHub() {
|
|
hubSyncStatus = 'loading'
|
|
hubSyncMessage = ''
|
|
try {
|
|
await JobService.runWaitResultScriptByPath({
|
|
workspace: 'admins',
|
|
path: 'u/admin/hub_sync',
|
|
requestBody: {}
|
|
})
|
|
hubSyncStatus = 'success'
|
|
hubSyncMessage = 'Resource types synced from hub successfully'
|
|
} catch (e: any) {
|
|
hubSyncMessage =
|
|
e?.body?.error?.message ||
|
|
e?.body?.message ||
|
|
(typeof e?.body === 'string' ? e.body : null) ||
|
|
e?.message ||
|
|
'Failed to sync from hub'
|
|
hubSyncStatus = 'error'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{#if loaded}
|
|
{#if showHubSync}
|
|
<div
|
|
class="p-3 border rounded-md bg-surface-secondary mb-4 mt-4 flex items-center justify-between gap-4"
|
|
>
|
|
<div>
|
|
<p class="text-xs font-medium text-secondary">Resource types</p>
|
|
<p class="text-2xs text-tertiary mt-0.5">
|
|
AI providers require their resource types. Sync from the Hub if they are missing.
|
|
</p>
|
|
</div>
|
|
<Button
|
|
variant="default"
|
|
unifiedSize="sm"
|
|
loading={hubSyncStatus === 'loading'}
|
|
onClick={syncFromHub}
|
|
>
|
|
Sync from hub
|
|
</Button>
|
|
</div>
|
|
{#if hubSyncStatus === 'success'}
|
|
<div class="mb-4">
|
|
<Alert type="success" title="Resource types synced">
|
|
{hubSyncMessage}
|
|
</Alert>
|
|
</div>
|
|
{:else if hubSyncStatus === 'error'}
|
|
<div class="mb-4">
|
|
<Alert type="error" title="Sync failed">
|
|
{hubSyncMessage}
|
|
</Alert>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
|
|
<AISettings
|
|
bind:this={aiSettings}
|
|
bind:hasUnsavedChanges
|
|
{initialConfig}
|
|
workspace="admins"
|
|
{disableChatOffset}
|
|
title="Windmill AI"
|
|
description="Windmill AI integrates with your favorite AI providers and models. Set your AI settings at the instance level to be able to use them on all your workspaces. Workspace-level settings can override these."
|
|
link="https://www.windmill.dev/docs/core_concepts/ai_generation"
|
|
promptScope="instance"
|
|
customSave={handleCustomSave}
|
|
/>
|
|
{/if}
|