mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
feat: add dynamic model discovery and routing for OpenCode provider
- Update isOpencodeModel() to detect dynamic models with provider/model format (e.g., github-copilot/gpt-4o, google/gemini-2.5-pro, zai-coding-plan/glm-4.7) - Update resolveModelString() to recognize and pass through OpenCode models - Update enhance route to route OpenCode models to OpenCode provider - Fix OpenCode CLI command format: use --format json (not stream-json) - Remove unsupported -q and - flags from CLI arguments - Update normalizeEvent() to handle actual OpenCode JSON event format - Add dynamic model configuration UI with provider grouping - Cache providers and models in app store for snappier navigation - Show authenticated providers in OpenCode CLI status card Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
DhanushSantosh
parent
ed65f70315
commit
6c5206daf4
@@ -1,17 +1,13 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import {
|
||||
OpencodeCliStatus,
|
||||
OpencodeCliStatusSkeleton,
|
||||
OpencodeModelConfigSkeleton,
|
||||
} from '../cli-status/opencode-cli-status';
|
||||
import { OpencodeCliStatus, OpencodeCliStatusSkeleton } from '../cli-status/opencode-cli-status';
|
||||
import { OpencodeModelConfiguration } from './opencode-model-configuration';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import type { CliStatus as SharedCliStatus } from '../shared/types';
|
||||
import type { OpencodeModelId } from '@automaker/types';
|
||||
import type { OpencodeAuthStatus } from '../cli-status/opencode-cli-status';
|
||||
import type { OpencodeAuthStatus, OpenCodeProviderInfo } from '../cli-status/opencode-cli-status';
|
||||
|
||||
const logger = createLogger('OpencodeSettings');
|
||||
|
||||
@@ -21,15 +17,21 @@ export function OpencodeSettingsTab() {
|
||||
opencodeDefaultModel,
|
||||
setOpencodeDefaultModel,
|
||||
toggleOpencodeModel,
|
||||
setDynamicOpencodeModels,
|
||||
dynamicOpencodeModels,
|
||||
enabledDynamicModelIds,
|
||||
toggleDynamicModel,
|
||||
cachedOpencodeProviders,
|
||||
setCachedOpencodeProviders,
|
||||
} = useAppStore();
|
||||
|
||||
const [isCheckingOpencodeCli, setIsCheckingOpencodeCli] = useState(false);
|
||||
const [isInitialLoading, setIsInitialLoading] = useState(true);
|
||||
const [isLoadingDynamicModels, setIsLoadingDynamicModels] = useState(false);
|
||||
const [cliStatus, setCliStatus] = useState<SharedCliStatus | null>(null);
|
||||
const [authStatus, setAuthStatus] = useState<OpencodeAuthStatus | null>(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
// Load OpenCode CLI status on mount
|
||||
// Phase 1: Load CLI status quickly on mount
|
||||
useEffect(() => {
|
||||
const checkOpencodeStatus = async () => {
|
||||
setIsCheckingOpencodeCli(true);
|
||||
@@ -46,7 +48,6 @@ export function OpencodeSettingsTab() {
|
||||
recommendation: result.recommendation,
|
||||
installCommands: result.installCommands,
|
||||
});
|
||||
// Set auth status if available
|
||||
if (result.auth) {
|
||||
setAuthStatus({
|
||||
authenticated: result.auth.authenticated,
|
||||
@@ -57,7 +58,6 @@ export function OpencodeSettingsTab() {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Fallback for web mode or when API is not available
|
||||
setCliStatus({
|
||||
success: false,
|
||||
status: 'not_installed',
|
||||
@@ -73,14 +73,54 @@ export function OpencodeSettingsTab() {
|
||||
});
|
||||
} finally {
|
||||
setIsCheckingOpencodeCli(false);
|
||||
setIsInitialLoading(false);
|
||||
}
|
||||
};
|
||||
checkOpencodeStatus();
|
||||
}, []);
|
||||
|
||||
// Phase 2: Load dynamic models and providers in background (only if not cached)
|
||||
useEffect(() => {
|
||||
const loadDynamicContent = async () => {
|
||||
const api = getElectronAPI();
|
||||
const isInstalled = cliStatus?.success && cliStatus?.status === 'installed';
|
||||
|
||||
if (!isInstalled || !api?.setup) return;
|
||||
|
||||
// Skip if already have cached data
|
||||
const needsProviders = cachedOpencodeProviders.length === 0;
|
||||
const needsModels = dynamicOpencodeModels.length === 0;
|
||||
|
||||
if (!needsProviders && !needsModels) return;
|
||||
|
||||
setIsLoadingDynamicModels(true);
|
||||
try {
|
||||
// Load providers if needed
|
||||
if (needsProviders && api.setup.getOpencodeProviders) {
|
||||
const providersResult = await api.setup.getOpencodeProviders();
|
||||
if (providersResult.success && providersResult.providers) {
|
||||
setCachedOpencodeProviders(providersResult.providers);
|
||||
}
|
||||
}
|
||||
|
||||
// Load models if needed
|
||||
if (needsModels && api.setup.getOpencodeModels) {
|
||||
const modelsResult = await api.setup.getOpencodeModels();
|
||||
if (modelsResult.success && modelsResult.models) {
|
||||
setDynamicOpencodeModels(modelsResult.models);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to load dynamic content:', error);
|
||||
} finally {
|
||||
setIsLoadingDynamicModels(false);
|
||||
}
|
||||
};
|
||||
loadDynamicContent();
|
||||
}, [cliStatus?.success, cliStatus?.status]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const handleRefreshOpencodeCli = useCallback(async () => {
|
||||
setIsCheckingOpencodeCli(true);
|
||||
setIsLoadingDynamicModels(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (api?.setup?.getOpencodeStatus) {
|
||||
@@ -94,7 +134,6 @@ export function OpencodeSettingsTab() {
|
||||
recommendation: result.recommendation,
|
||||
installCommands: result.installCommands,
|
||||
});
|
||||
// Update auth status if available
|
||||
if (result.auth) {
|
||||
setAuthStatus({
|
||||
authenticated: result.auth.authenticated,
|
||||
@@ -104,14 +143,35 @@ export function OpencodeSettingsTab() {
|
||||
hasOAuthToken: result.auth.hasOAuthToken,
|
||||
});
|
||||
}
|
||||
|
||||
if (result.installed) {
|
||||
// Refresh providers
|
||||
if (api?.setup?.getOpencodeProviders) {
|
||||
const providersResult = await api.setup.getOpencodeProviders();
|
||||
if (providersResult.success && providersResult.providers) {
|
||||
setCachedOpencodeProviders(providersResult.providers);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh dynamic models
|
||||
if (api?.setup?.refreshOpencodeModels) {
|
||||
const modelsResult = await api.setup.refreshOpencodeModels();
|
||||
if (modelsResult.success && modelsResult.models) {
|
||||
setDynamicOpencodeModels(modelsResult.models);
|
||||
}
|
||||
}
|
||||
|
||||
toast.success('OpenCode CLI refreshed');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to refresh OpenCode CLI status:', error);
|
||||
toast.error('Failed to refresh OpenCode CLI status');
|
||||
} finally {
|
||||
setIsCheckingOpencodeCli(false);
|
||||
setIsLoadingDynamicModels(false);
|
||||
}
|
||||
}, []);
|
||||
}, [setDynamicOpencodeModels, setCachedOpencodeProviders]);
|
||||
|
||||
const handleDefaultModelChange = useCallback(
|
||||
(model: OpencodeModelId) => {
|
||||
@@ -142,12 +202,25 @@ export function OpencodeSettingsTab() {
|
||||
[toggleOpencodeModel]
|
||||
);
|
||||
|
||||
// Show loading skeleton during initial load
|
||||
if (isInitialLoading) {
|
||||
const handleDynamicModelToggle = useCallback(
|
||||
(modelId: string, enabled: boolean) => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
toggleDynamicModel(modelId, enabled);
|
||||
} catch (error) {
|
||||
toast.error('Failed to update dynamic model');
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
},
|
||||
[toggleDynamicModel]
|
||||
);
|
||||
|
||||
// Show skeleton only while checking CLI status initially
|
||||
if (!cliStatus && isCheckingOpencodeCli) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<OpencodeCliStatusSkeleton />
|
||||
<OpencodeModelConfigSkeleton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -159,6 +232,7 @@ export function OpencodeSettingsTab() {
|
||||
<OpencodeCliStatus
|
||||
status={cliStatus}
|
||||
authStatus={authStatus}
|
||||
providers={cachedOpencodeProviders as OpenCodeProviderInfo[]}
|
||||
isChecking={isCheckingOpencodeCli}
|
||||
onRefresh={handleRefreshOpencodeCli}
|
||||
/>
|
||||
@@ -171,6 +245,10 @@ export function OpencodeSettingsTab() {
|
||||
isSaving={isSaving}
|
||||
onDefaultModelChange={handleDefaultModelChange}
|
||||
onModelToggle={handleModelToggle}
|
||||
dynamicModels={dynamicOpencodeModels}
|
||||
enabledDynamicModelIds={enabledDynamicModelIds}
|
||||
onDynamicModelToggle={handleDynamicModelToggle}
|
||||
isLoadingDynamicModels={isLoadingDynamicModels}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user