Refine desktop setup and remove bundled app center apps
This commit is contained in:
@@ -2,6 +2,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import {
|
||||
YINIAN_MODEL_AUTH_PROFILE_ID,
|
||||
YINIAN_MODEL_DEFAULT_BASE_URL,
|
||||
YINIAN_MODEL_PROVIDER_KEY,
|
||||
} from '../../shared/yinian-model';
|
||||
|
||||
const modelDiagnosticsMocks = vi.hoisted(() => ({
|
||||
config: {} as Record<string, unknown>,
|
||||
@@ -86,7 +91,7 @@ describe('Yinian model diagnostics', () => {
|
||||
rmSync(modelDiagnosticsMocks.testOpenClawDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('repairs runtime model defaults and normalizes MiniMax auth profile aliases', async () => {
|
||||
it('does not seed legacy runtime model defaults as the generic model API provider', async () => {
|
||||
const {
|
||||
buildYinianModelConfigDiagnostics,
|
||||
ensureYinianModelRuntimeConfigured,
|
||||
@@ -94,35 +99,241 @@ describe('Yinian model diagnostics', () => {
|
||||
|
||||
await ensureYinianModelRuntimeConfigured();
|
||||
|
||||
expect(modelDiagnosticsMocks.writeOpenClawConfig).toHaveBeenCalledWith(expect.objectContaining({
|
||||
models: expect.objectContaining({
|
||||
providers: expect.objectContaining({
|
||||
minimax: expect.not.objectContaining({ timeoutSeconds: expect.anything() }),
|
||||
}),
|
||||
}),
|
||||
agents: expect.objectContaining({
|
||||
defaults: expect.objectContaining({
|
||||
heartbeat: expect.objectContaining({ every: '0m' }),
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
const config = modelDiagnosticsMocks.config as {
|
||||
models: {
|
||||
mode?: string;
|
||||
pricing?: unknown;
|
||||
providers: Record<string, { timeoutSeconds?: number }>;
|
||||
};
|
||||
agents: {
|
||||
defaults: {
|
||||
heartbeat?: { every?: string };
|
||||
model: { primary?: string; fallbacks?: string[] };
|
||||
};
|
||||
};
|
||||
};
|
||||
expect(config.models.mode).toBe('merge');
|
||||
expect(config.models.pricing).toBeUndefined();
|
||||
expect(config.models.providers.minimax.timeoutSeconds).toBeUndefined();
|
||||
expect(config.models.providers[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(config.agents.defaults.model.primary).toBe('minimax/MiniMax-M2.7');
|
||||
expect(config.agents.defaults.model.fallbacks).toEqual(['minimax/MiniMax-M2.5']);
|
||||
expect(config.agents.defaults.heartbeat?.every).toBe('0m');
|
||||
|
||||
const authStore = JSON.parse(readFileSync(authProfilesPath(testHome), 'utf8')) as {
|
||||
profiles: Record<string, { provider?: string; key?: string }>;
|
||||
order?: Record<string, string[]>;
|
||||
lastGood?: Record<string, string>;
|
||||
};
|
||||
expect(authStore.profiles['minimax:default']).toMatchObject({
|
||||
provider: 'minimax',
|
||||
key: 'secret',
|
||||
});
|
||||
expect(authStore.order?.minimax).toContain('minimax:default');
|
||||
expect(authStore.lastGood?.minimax).toBe('minimax:default');
|
||||
expect(authStore.profiles[YINIAN_MODEL_AUTH_PROFILE_ID]).toBeUndefined();
|
||||
expect(authStore.order?.[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(authStore.lastGood?.[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
|
||||
const diagnostics = await buildYinianModelConfigDiagnostics();
|
||||
expect(diagnostics.model.primary).toBe('minimax/MiniMax-M2.7');
|
||||
expect(diagnostics.model.providerKey).toBe('minimax');
|
||||
expect(diagnostics.model.fallbacks).toEqual(['minimax/MiniMax-M2.5']);
|
||||
expect(diagnostics.runtime.heartbeatDisabled).toBe(true);
|
||||
expect(diagnostics.providers.map((provider) => provider.key)).toEqual(['minimax']);
|
||||
expect(diagnostics.providers.some((provider) => provider.key === YINIAN_MODEL_PROVIDER_KEY)).toBe(false);
|
||||
});
|
||||
|
||||
it('removes the legacy placeholder model provider and auth leftovers', async () => {
|
||||
modelDiagnosticsMocks.config = {
|
||||
models: {
|
||||
providers: {
|
||||
[YINIAN_MODEL_PROVIDER_KEY]: {
|
||||
baseUrl: YINIAN_MODEL_DEFAULT_BASE_URL,
|
||||
api: 'openai-completions',
|
||||
timeoutSeconds: 30,
|
||||
models: [{ id: 'custom-model', name: 'Custom Model' }],
|
||||
},
|
||||
deepseek: {
|
||||
baseUrl: 'https://api.deepseek.com/v1',
|
||||
api: 'openai-completions',
|
||||
models: [{ id: 'deepseek-v4-pro' }],
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: `${YINIAN_MODEL_PROVIDER_KEY}/custom-model`,
|
||||
fallbacks: [`${YINIAN_MODEL_PROVIDER_KEY}/custom-model-lite`],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
writeFileSync(authProfilesPath(testHome), JSON.stringify({
|
||||
version: 1,
|
||||
profiles: {
|
||||
[YINIAN_MODEL_AUTH_PROFILE_ID]: {
|
||||
type: 'api_key',
|
||||
provider: YINIAN_MODEL_PROVIDER_KEY,
|
||||
key: 'placeholder-secret',
|
||||
},
|
||||
[`${YINIAN_MODEL_PROVIDER_KEY}:legacy`]: {
|
||||
type: 'api_key',
|
||||
provider: YINIAN_MODEL_PROVIDER_KEY,
|
||||
key: 'legacy-secret',
|
||||
},
|
||||
'deepseek:default': {
|
||||
type: 'api_key',
|
||||
provider: 'deepseek',
|
||||
key: 'deepseek-secret',
|
||||
},
|
||||
},
|
||||
order: {
|
||||
[YINIAN_MODEL_PROVIDER_KEY]: [YINIAN_MODEL_AUTH_PROFILE_ID],
|
||||
deepseek: ['deepseek:default'],
|
||||
},
|
||||
lastGood: {
|
||||
[YINIAN_MODEL_PROVIDER_KEY]: YINIAN_MODEL_AUTH_PROFILE_ID,
|
||||
deepseek: 'deepseek:default',
|
||||
},
|
||||
}, null, 2));
|
||||
|
||||
const {
|
||||
buildYinianModelConfigDiagnostics,
|
||||
ensureYinianModelRuntimeConfigured,
|
||||
} = await import('@electron/utils/model-diagnostics');
|
||||
|
||||
await ensureYinianModelRuntimeConfigured();
|
||||
|
||||
const config = modelDiagnosticsMocks.config as {
|
||||
models: { providers: Record<string, unknown> };
|
||||
agents: { defaults: { model: { primary?: string; fallbacks?: string[] } } };
|
||||
};
|
||||
expect(config.models.providers[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(config.models.providers.deepseek).toEqual(expect.objectContaining({
|
||||
baseUrl: 'https://api.deepseek.com/v1',
|
||||
}));
|
||||
expect(config.agents.defaults.model.primary).toBeUndefined();
|
||||
expect(config.agents.defaults.model.fallbacks).toEqual([]);
|
||||
|
||||
const authStore = JSON.parse(readFileSync(authProfilesPath(testHome), 'utf8')) as {
|
||||
profiles: Record<string, unknown>;
|
||||
order?: Record<string, string[]>;
|
||||
lastGood?: Record<string, string>;
|
||||
};
|
||||
expect(authStore.profiles[YINIAN_MODEL_AUTH_PROFILE_ID]).toBeUndefined();
|
||||
expect(authStore.profiles[`${YINIAN_MODEL_PROVIDER_KEY}:legacy`]).toBeUndefined();
|
||||
expect(authStore.profiles['deepseek:default']).toEqual(expect.objectContaining({
|
||||
provider: 'deepseek',
|
||||
}));
|
||||
expect(authStore.order?.[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(authStore.order?.deepseek).toEqual(['deepseek:default']);
|
||||
expect(authStore.lastGood?.[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(authStore.lastGood?.deepseek).toBe('deepseek:default');
|
||||
|
||||
const diagnostics = await buildYinianModelConfigDiagnostics();
|
||||
expect(diagnostics.model.primary).toBeNull();
|
||||
expect(diagnostics.providers).toEqual([]);
|
||||
expect(diagnostics.authProfiles.providers).toEqual([]);
|
||||
});
|
||||
|
||||
it('preserves an existing custom default model during runtime repair', async () => {
|
||||
modelDiagnosticsMocks.config = {
|
||||
models: {
|
||||
providers: {
|
||||
'custom-main': {
|
||||
baseUrl: 'https://llm.example.test/v1',
|
||||
api: 'openai-completions',
|
||||
models: [{ id: 'tenant-model' }],
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: 'custom-main/tenant-model',
|
||||
fallbacks: ['custom-main/tenant-model-lite'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
writeFileSync(authProfilesPath(testHome), JSON.stringify({
|
||||
version: 1,
|
||||
profiles: {
|
||||
'custom-main:default': {
|
||||
type: 'api_key',
|
||||
provider: 'custom-main',
|
||||
key: 'custom-secret',
|
||||
},
|
||||
},
|
||||
}, null, 2));
|
||||
const {
|
||||
buildYinianModelConfigDiagnostics,
|
||||
ensureYinianModelRuntimeConfigured,
|
||||
} = await import('@electron/utils/model-diagnostics');
|
||||
|
||||
await ensureYinianModelRuntimeConfigured();
|
||||
|
||||
const config = modelDiagnosticsMocks.config as {
|
||||
models: { providers: Record<string, unknown> };
|
||||
agents: { defaults: { model: { primary: string; fallbacks: string[] } } };
|
||||
};
|
||||
expect(config.models.providers[YINIAN_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(config.agents.defaults.model.primary).toBe('custom-main/tenant-model');
|
||||
expect(config.agents.defaults.model.fallbacks).toEqual(['custom-main/tenant-model-lite']);
|
||||
|
||||
const diagnostics = await buildYinianModelConfigDiagnostics();
|
||||
expect(diagnostics.ok).toBe(true);
|
||||
expect(diagnostics.model.primary).toBe('minimax/MiniMax-M2.7');
|
||||
expect(diagnostics.runtime.heartbeatDisabled).toBe(true);
|
||||
expect(diagnostics.checks.find((check) => check.id === 'auth-profile')?.status).toBe('ok');
|
||||
expect(diagnostics.model.providerKey).toBe('custom-main');
|
||||
expect(diagnostics.providers.map((provider) => provider.key)).toEqual(['custom-main']);
|
||||
expect(diagnostics.authProfiles.providers.map((provider) => provider.provider)).toEqual(['custom-main']);
|
||||
});
|
||||
|
||||
it('reports only the active DeepSeek provider when other configured providers exist', async () => {
|
||||
modelDiagnosticsMocks.config = {
|
||||
models: {
|
||||
providers: {
|
||||
'minimax-portal': {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
models: [{ id: 'MiniMax-M3' }],
|
||||
},
|
||||
deepseek: {
|
||||
baseUrl: 'https://api.deepseek.com/v1',
|
||||
api: 'openai-completions',
|
||||
models: [{ id: 'deepseek-v4-pro' }],
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: 'deepseek/deepseek-v4-pro',
|
||||
fallbacks: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
writeFileSync(authProfilesPath(testHome), JSON.stringify({
|
||||
version: 1,
|
||||
profiles: {
|
||||
'minimax-portal:default': {
|
||||
type: 'api_key',
|
||||
provider: 'minimax-portal',
|
||||
key: 'minimax-secret',
|
||||
},
|
||||
'deepseek:default': {
|
||||
type: 'api_key',
|
||||
provider: 'deepseek',
|
||||
key: 'deepseek-secret',
|
||||
},
|
||||
},
|
||||
}, null, 2));
|
||||
|
||||
const {
|
||||
buildYinianModelConfigDiagnostics,
|
||||
} = await import('@electron/utils/model-diagnostics');
|
||||
|
||||
const diagnostics = await buildYinianModelConfigDiagnostics();
|
||||
|
||||
expect(diagnostics.ok).toBe(true);
|
||||
expect(diagnostics.model.primary).toBe('deepseek/deepseek-v4-pro');
|
||||
expect(diagnostics.providers.map((provider) => provider.key)).toEqual(['deepseek']);
|
||||
expect(diagnostics.authProfiles.providers.map((provider) => provider.provider)).toEqual(['deepseek']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user