fix: remove CORS checking for downloading remote model file

This commit is contained in:
Xiaohan-Tian
2026-05-15 12:53:36 -07:00
parent 1fd2c283f2
commit b2bbc08761
7 changed files with 212 additions and 46 deletions
+11 -2
View File
@@ -339,6 +339,10 @@ const ChatBox: React.FC<ChatBoxProps> = ({ isVisible }) => {
}
}, [isProcessing]);
const localRuntimeMessage = localModelState.runtimeSupport.reason;
const hasLocalRuntimeHardFailure = !localModelState.runtimeSupport.supported;
const hasLocalRuntimeWarning = localModelState.runtimeSupport.supported && !!localRuntimeMessage;
return (
<div className={`chatbox ${isVisible ? '' : 'is-hidden'}`}>
<div className="chatbox-header">
@@ -391,9 +395,14 @@ const ChatBox: React.FC<ChatBoxProps> = ({ isVisible }) => {
<div className="chatbox-local-runtime-section">
<div className="chatbox-local-runtime-card">
<h4 className="chatbox-local-mode-title">{LOCAL_LLM_DISPLAY_NAME} Local Runtime</h4>
{!localModelState.runtimeSupport.supported && (
{hasLocalRuntimeWarning && (
<div className="chatbox-local-runtime-warning">
{localModelState.runtimeSupport.reason}
{localRuntimeMessage}
</div>
)}
{hasLocalRuntimeHardFailure && (
<div className="chatbox-local-runtime-error">
{localRuntimeMessage}
</div>
)}
{!localModelState.isCached && !localModelState.isDownloading && localModelState.runtimeSupport.supported && (
@@ -25,6 +25,24 @@ const configState = new Map<string, unknown>([
['general.kgone.base_url', 'http://127.0.0.1:8000'],
]);
const localModelState = {
isCached: false,
isChecking: false,
isDownloading: false,
isDeleting: false,
progressPercent: 0,
progressText: '',
error: '',
runtimeSupport: {
supported: true,
webgpuExposed: true,
crossOriginIsolated: true,
sharedArrayBufferAvailable: true,
secureContext: true,
reason: null as string | null,
},
};
const configManagerMock = {
getIsInitialized: vi.fn(() => true),
initialize: vi.fn().mockResolvedValue(undefined),
@@ -44,41 +62,9 @@ vi.mock('../../../core/config/ConfigManager', () => ({
vi.mock('../../../util/localLLMModelManager', () => ({
LocalLLMModelManager: {
getState: () => ({
isCached: false,
isChecking: false,
isDownloading: false,
isDeleting: false,
progressPercent: 0,
progressText: '',
error: '',
runtimeSupport: {
supported: true,
webgpuExposed: true,
crossOriginIsolated: true,
sharedArrayBufferAvailable: true,
secureContext: true,
reason: null,
},
}),
getState: () => localModelState,
subscribe: (listener: (state: unknown) => void) => {
listener({
isCached: false,
isChecking: false,
isDownloading: false,
isDeleting: false,
progressPercent: 0,
progressText: '',
error: '',
runtimeSupport: {
supported: true,
webgpuExposed: true,
crossOriginIsolated: true,
sharedArrayBufferAvailable: true,
secureContext: true,
reason: null,
},
});
listener(localModelState);
return () => {};
},
deleteCachedModel: vi.fn().mockResolvedValue(undefined),
@@ -90,6 +76,21 @@ describe('GeneralSettings', () => {
configState.set('general.local_browser.context_length', 65536);
configManagerMock.get.mockClear();
configManagerMock.set.mockClear();
localModelState.isCached = false;
localModelState.isChecking = false;
localModelState.isDownloading = false;
localModelState.isDeleting = false;
localModelState.progressPercent = 0;
localModelState.progressText = '';
localModelState.error = '';
localModelState.runtimeSupport = {
supported: true,
webgpuExposed: true,
crossOriginIsolated: true,
sharedArrayBufferAvailable: true,
secureContext: true,
reason: null,
};
});
it('renders the local context length selector and VRAM hint', async () => {
@@ -117,4 +118,20 @@ describe('GeneralSettings', () => {
expect(configManagerMock.set).toHaveBeenCalledWith('general.local_browser.context_length', 131072);
});
});
it('shows a warning when runtime may fail on this host but is still allowed', async () => {
localModelState.runtimeSupport = {
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.',
};
render(<GeneralSettings />);
expect(await screen.findByText(/may not support the local browser runtime reliably/i)).toBeTruthy();
expect(screen.getByText(/The local model downloads automatically/i)).toBeTruthy();
});
});
@@ -232,6 +232,10 @@ const GeneralSettings: React.FC = () => {
}
};
const localRuntimeMessage = localModelState.runtimeSupport.reason;
const hasLocalRuntimeHardFailure = !localModelState.runtimeSupport.supported;
const hasLocalRuntimeWarning = localModelState.runtimeSupport.supported && !!localRuntimeMessage;
// NOTE: Gemini and Claude are not supported yet due to CORS issues.
return (
<div className="settings-section">
@@ -282,9 +286,15 @@ const GeneralSettings: React.FC = () => {
<div className="settings-group">
<h4>{LOCAL_LLM_DISPLAY_NAME} Local Runtime</h4>
{!localModelState.runtimeSupport.supported && (
{hasLocalRuntimeWarning && (
<div className="settings-help" style={{ fontSize: '12px', color: '#d0a56b', marginTop: '4px', marginBottom: '8px' }}>
{localModelState.runtimeSupport.reason}
{localRuntimeMessage}
</div>
)}
{hasLocalRuntimeHardFailure && (
<div className="settings-help" style={{ fontSize: '12px', color: '#d45a5a', marginTop: '4px', marginBottom: '8px' }}>
{localRuntimeMessage}
</div>
)}