fix: remove CORS checking for downloading remote model file
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
detectLocalLLMRuntimeSupport,
|
||||
formatLocalLLMContextLength,
|
||||
LOCAL_LLM_DEFAULT_CONTEXT_LENGTH,
|
||||
normalizeLocalLLMContextLength,
|
||||
@@ -16,4 +17,40 @@ describe('localLLMConfig', () => {
|
||||
expect(formatLocalLLMContextLength(65536)).toBe('64k');
|
||||
expect(formatLocalLLMContextLength(131072)).toBe('128k');
|
||||
});
|
||||
|
||||
it('marks insecure contexts as unsupported', () => {
|
||||
Object.defineProperty(window, 'isSecureContext', { value: false, configurable: true });
|
||||
Object.defineProperty(window, 'crossOriginIsolated', { value: true, configurable: true });
|
||||
Object.defineProperty(globalThis, 'SharedArrayBuffer', { value: class SharedArrayBuffer {}, configurable: true });
|
||||
Object.defineProperty(globalThis, 'navigator', { value: { gpu: {} }, configurable: true });
|
||||
|
||||
const support = detectLocalLLMRuntimeSupport();
|
||||
|
||||
expect(support.supported).toBe(false);
|
||||
expect(support.reason).toContain('secure context');
|
||||
});
|
||||
|
||||
it('marks missing WebGPU as unsupported', () => {
|
||||
Object.defineProperty(window, 'isSecureContext', { value: true, configurable: true });
|
||||
Object.defineProperty(window, 'crossOriginIsolated', { value: true, configurable: true });
|
||||
Object.defineProperty(globalThis, 'SharedArrayBuffer', { value: class SharedArrayBuffer {}, configurable: true });
|
||||
Object.defineProperty(globalThis, 'navigator', { value: {}, configurable: true });
|
||||
|
||||
const support = detectLocalLLMRuntimeSupport();
|
||||
|
||||
expect(support.supported).toBe(false);
|
||||
expect(support.reason).toContain('WebGPU');
|
||||
});
|
||||
|
||||
it('allows runtime attempts when only SharedArrayBuffer isolation support is missing', () => {
|
||||
Object.defineProperty(window, 'isSecureContext', { value: true, configurable: true });
|
||||
Object.defineProperty(window, 'crossOriginIsolated', { value: false, configurable: true });
|
||||
Object.defineProperty(globalThis, 'SharedArrayBuffer', { value: undefined, configurable: true });
|
||||
Object.defineProperty(globalThis, 'navigator', { value: { gpu: {} }, configurable: true });
|
||||
|
||||
const support = detectLocalLLMRuntimeSupport();
|
||||
|
||||
expect(support.supported).toBe(true);
|
||||
expect(support.reason).toContain('cross-origin isolation');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,14 +29,14 @@ export function detectLocalLLMRuntimeSupport(): LocalLLMRuntimeSupport {
|
||||
let reason: string | null = null;
|
||||
if (!secureContext) {
|
||||
reason = 'Local browser LLM requires a secure context (HTTPS or localhost).';
|
||||
} else if (!crossOriginIsolated || !sharedArrayBufferAvailable) {
|
||||
reason = 'Local browser LLM requires SharedArrayBuffer support. Ensure COOP/COEP headers are enabled.';
|
||||
} else if (!webgpuExposed) {
|
||||
reason = 'Local browser LLM currently requires a browser with WebGPU support.';
|
||||
} else if (!crossOriginIsolated || !sharedArrayBufferAvailable) {
|
||||
reason = 'This host may not support the local browser runtime reliably because cross-origin isolation or SharedArrayBuffer is unavailable. COOP/COEP headers may be missing.';
|
||||
}
|
||||
|
||||
return {
|
||||
supported: reason === null,
|
||||
supported: secureContext && webgpuExposed,
|
||||
webgpuExposed,
|
||||
crossOriginIsolated,
|
||||
sharedArrayBufferAvailable,
|
||||
|
||||
@@ -2,7 +2,16 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { processUserMessage } from './UserMessageFilter';
|
||||
import { addWelcomeMessage } from '../../utils/chatMessageUtils';
|
||||
|
||||
const { detectLocalLLMRuntimeSupportMock } = vi.hoisted(() => ({
|
||||
detectLocalLLMRuntimeSupportMock: vi.fn(),
|
||||
}));
|
||||
|
||||
const configState = new Map<string, unknown>();
|
||||
const storeState = {
|
||||
setStatus: vi.fn(),
|
||||
activeRegionId: null as string | null,
|
||||
selectedRegionIds: [] as string[],
|
||||
};
|
||||
|
||||
const configManagerMock = {
|
||||
getIsInitialized: vi.fn(() => true),
|
||||
@@ -22,11 +31,7 @@ vi.mock('../chatUtil', () => ({
|
||||
|
||||
vi.mock('../../stores/projectStore', () => ({
|
||||
useProjectStore: {
|
||||
getState: () => ({
|
||||
setStatus: vi.fn(),
|
||||
activeRegionId: null,
|
||||
selectedRegionIds: [],
|
||||
}),
|
||||
getState: () => storeState,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -36,6 +41,14 @@ vi.mock('../../agent/core/SystemPrompts', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../localLLMConfig', async () => {
|
||||
const actual = await vi.importActual<typeof import('../localLLMConfig')>('../localLLMConfig');
|
||||
return {
|
||||
...actual,
|
||||
detectLocalLLMRuntimeSupport: detectLocalLLMRuntimeSupportMock,
|
||||
};
|
||||
});
|
||||
|
||||
describe('processUserMessage /welcome', () => {
|
||||
beforeEach(() => {
|
||||
configState.clear();
|
||||
@@ -50,6 +63,18 @@ describe('processUserMessage /welcome', () => {
|
||||
configManagerMock.getIsInitialized.mockReturnValue(true);
|
||||
configManagerMock.initialize.mockClear();
|
||||
configManagerMock.get.mockClear();
|
||||
storeState.setStatus.mockClear();
|
||||
storeState.activeRegionId = null;
|
||||
storeState.selectedRegionIds = [];
|
||||
detectLocalLLMRuntimeSupportMock.mockReset();
|
||||
detectLocalLLMRuntimeSupportMock.mockReturnValue({
|
||||
supported: true,
|
||||
webgpuExposed: true,
|
||||
crossOriginIsolated: true,
|
||||
sharedArrayBufferAvailable: true,
|
||||
secureContext: true,
|
||||
reason: null,
|
||||
});
|
||||
|
||||
vi.stubGlobal('fetch', vi.fn(async (input: string | URL | Request) => {
|
||||
const url = String(input);
|
||||
@@ -106,4 +131,39 @@ describe('processUserMessage /welcome', () => {
|
||||
expect(message?.role).toBe('assistant');
|
||||
expect(message?.content).toContain('welcome_local_llm.md');
|
||||
});
|
||||
|
||||
it('blocks local-browser messages when the runtime is hard unsupported', async () => {
|
||||
detectLocalLLMRuntimeSupportMock.mockReturnValue({
|
||||
supported: false,
|
||||
webgpuExposed: false,
|
||||
crossOriginIsolated: false,
|
||||
sharedArrayBufferAvailable: false,
|
||||
secureContext: true,
|
||||
reason: 'Local browser LLM currently requires a browser with WebGPU support.',
|
||||
});
|
||||
|
||||
const result = await processUserMessage('hello');
|
||||
|
||||
expect(result.sendToLLM).toBe(false);
|
||||
expect(result.pseudoAssistantResponse).toContain('WebGPU');
|
||||
expect(result.metadata).toMatchObject({ error: 'local_browser_unsupported' });
|
||||
});
|
||||
|
||||
it('allows local-browser messages when only SharedArrayBuffer isolation support is missing', async () => {
|
||||
detectLocalLLMRuntimeSupportMock.mockReturnValue({
|
||||
supported: true,
|
||||
webgpuExposed: true,
|
||||
crossOriginIsolated: false,
|
||||
sharedArrayBufferAvailable: false,
|
||||
secureContext: true,
|
||||
reason: 'This host may not support the local browser runtime reliably because cross-origin isolation or SharedArrayBuffer is unavailable. COOP/COEP headers may be missing.',
|
||||
});
|
||||
storeState.activeRegionId = 'region-1';
|
||||
|
||||
const result = await processUserMessage('hello');
|
||||
|
||||
expect(result.sendToLLM).toBe(true);
|
||||
expect(result.finalMessageForLLM).toContain('hello');
|
||||
expect(result.pseudoAssistantResponse).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user