Addressing PR comments#7
Conversation
Co-authored-by: raux <5723643+raux@users.noreply.github.com>
…lity Co-authored-by: raux <5723643+raux@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request adds a "Test Connection" feature to the BonsAIDE application, allowing users to verify connectivity to their LM Studio server before performing operations. The feature is implemented for both the VSCode extension and standalone web server modes.
Changes:
- Adds test connection button and status display to the UI (webview and client interfaces)
- Implements backend logic to validate URL format and send a minimal test request to LM Studio
- Provides visual feedback (success/error/testing states) via color-coded status messages
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/server.ts | Adds testConnection command handler for standalone server mode with URL validation and LLM test request |
| src/extension.ts | Adds testConnection command handler for VSCode extension mode with URL validation and LLM test request |
| media/webview.html | Adds test connection button, status span, and styling for the VSCode extension webview UI |
| client/index.html | Adds test connection button and status span for the standalone web client UI |
| client/js/app.js | Implements click handler for test connection button and result message handling |
| client/css/styles.css | Adds CSS styling for connection test status states (testing, success, error) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <div style="margin:6px 0; display:flex; flex-wrap:wrap; gap:8px;"> | ||
| <div style="margin:6px 0; display:flex; flex-wrap:wrap; gap:8px; align-items:center;"> | ||
| <label>LM_Studio URL: | ||
| <input id="baseUrlInput" type="text" size="40" placeholder="http://localhost:1234/v1" /> |
There was a problem hiding this comment.
The placeholder value includes the protocol prefix "http://localhost:1234/v1", but the URL validation regex at line 563 in extension.ts expects URLs without the protocol (host:port/path pattern). This will cause the validation to fail if a user copies the placeholder value. The placeholder should be changed to "localhost:1234/v1" to match the expected format.
| <input id="baseUrlInput" type="text" size="40" placeholder="http://localhost:1234/v1" /> | |
| <input id="baseUrlInput" type="text" size="40" placeholder="localhost:1234/v1" /> |
| // Validate URL format (should be host:port/path pattern) | ||
| if (!/^[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | ||
| throw new Error('Invalid URL format. Expected format: host:port/path (e.g., localhost:1234/v1)'); | ||
| } | ||
|
|
||
| const res = await fetch(`http://${testUrl}/chat/completions`, { |
There was a problem hiding this comment.
The URL validation regex rejects URLs that contain the protocol prefix. However, looking at extension.ts line 899, there's code that uses a default URL with the "http://" protocol prefix ("http://10.184.201.141:1234/v1"). The regex should either be updated to optionally accept the protocol prefix, or all code should be consistent about expecting URLs without the protocol. Consider updating the regex to: /^(https?://)?[\w.-]+(:\d+)?(/[\w./]*)?$/ and strip the protocol before prepending "http://" in the fetch call.
| // Validate URL format (should be host:port/path pattern) | |
| if (!/^[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | |
| throw new Error('Invalid URL format. Expected format: host:port/path (e.g., localhost:1234/v1)'); | |
| } | |
| const res = await fetch(`http://${testUrl}/chat/completions`, { | |
| // Validate URL format (allows optional http/https protocol and host:port/path pattern) | |
| if (!/^(https?:\/\/)?[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | |
| throw new Error('Invalid URL format. Expected format: host:port/path (e.g., localhost:1234/v1) or with protocol (e.g., http://localhost:1234/v1)'); | |
| } | |
| const normalizedUrl = testUrl.replace(/^https?:\/\//, ''); | |
| const res = await fetch(`http://${normalizedUrl}/chat/completions`, { |
| // Validate URL format (should be host:port/path pattern) | ||
| if (!/^[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | ||
| throw new Error('Invalid URL format. Expected format: host:port/path (e.g., localhost:1234/v1)'); | ||
| } | ||
|
|
||
| const res = await fetch(`http://${testUrl}/chat/completions`, { |
There was a problem hiding this comment.
The URL validation regex rejects URLs that contain the protocol prefix. However, the media/webview.html placeholder shows "http://localhost:1234/v1" (with protocol). The regex should either be updated to optionally accept the protocol prefix, or all code should be consistent about expecting URLs without the protocol. Consider updating the regex to: /^(https?://)?[\w.-]+(:\d+)?(/[\w./]*)?$/ and strip the protocol before prepending "http://" in the fetch call.
| // Validate URL format (should be host:port/path pattern) | |
| if (!/^[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | |
| throw new Error('Invalid URL format. Expected format: host:port/path (e.g., localhost:1234/v1)'); | |
| } | |
| const res = await fetch(`http://${testUrl}/chat/completions`, { | |
| // Normalize and validate URL format (accept optional protocol, ensure host:port/path pattern) | |
| const normalizedTestUrl = testUrl.replace(/^https?:\/\//, ''); | |
| if (!/^(https?:\/\/)?[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | |
| throw new Error('Invalid URL format. Expected format similar to: localhost:1234/v1 or http://localhost:1234/v1'); | |
| } | |
| const res = await fetch(`http://${normalizedTestUrl}/chat/completions`, { |
| const res = await fetch(`http://${testUrl}/chat/completions`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': 'Bearer lm-studio', | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: testModel, | ||
| messages: [{ role: 'user', content: 'Say "connected" in one word.' }], | ||
| temperature: 0, | ||
| max_tokens: 10, | ||
| stream: false, | ||
| }), | ||
| }); | ||
|
|
There was a problem hiding this comment.
The connection test fetch request has no timeout configured. If the LM Studio server is unreachable or not responding, the user may have to wait for the default fetch timeout (which can be very long). Consider adding an AbortController with a reasonable timeout (e.g., 10 seconds) to provide better user experience.
| const res = await fetch(`http://${testUrl}/chat/completions`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer lm-studio', | |
| }, | |
| body: JSON.stringify({ | |
| model: testModel, | |
| messages: [{ role: 'user', content: 'Say "connected" in one word.' }], | |
| temperature: 0, | |
| max_tokens: 10, | |
| stream: false, | |
| }), | |
| }); | |
| const controller = new AbortController(); | |
| const timeoutMs = 10_000; | |
| const timeoutId = setTimeout(() => controller.abort(), timeoutMs); | |
| const res = await (async () => { | |
| try { | |
| return await fetch(`http://${testUrl}/chat/completions`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer lm-studio', | |
| }, | |
| body: JSON.stringify({ | |
| model: testModel, | |
| messages: [{ role: 'user', content: 'Say "connected" in one word.' }], | |
| temperature: 0, | |
| max_tokens: 10, | |
| stream: false, | |
| }), | |
| signal: controller.signal, | |
| }); | |
| } finally { | |
| clearTimeout(timeoutId); | |
| } | |
| })(); |
| const res = await fetch(`http://${testUrl}/chat/completions`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': 'Bearer lm-studio', | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: testModel, | ||
| messages: [{ role: 'user', content: 'Say "connected" in one word.' }], | ||
| temperature: 0, | ||
| max_tokens: 10, | ||
| stream: false, | ||
| }), | ||
| }); |
There was a problem hiding this comment.
The connection test fetch request has no timeout configured. If the LM Studio server is unreachable or not responding, the user may have to wait for the default fetch timeout (which can be very long). Consider adding an AbortController with a reasonable timeout (e.g., 10 seconds) to provide better user experience.
| if (message.command === 'testConnection') { | ||
| const testUrl = message.baseUrl || baseUrl; | ||
| const testModel = message.model || LLMmodel; | ||
| bonsaiLog('Testing connection to:', testUrl, 'with model:', testModel); | ||
| panel.webview.postMessage({ command: 'loading', text: 'Testing connection...' }); | ||
|
|
||
| try { | ||
| // Validate URL format (should be host:port/path pattern) | ||
| if (!/^[\w.-]+(:\d+)?(\/[\w./]*)?$/.test(testUrl)) { | ||
| throw new Error('Invalid URL format. Expected format: host:port/path (e.g., localhost:1234/v1)'); | ||
| } | ||
|
|
||
| const res = await fetch(`http://${testUrl}/chat/completions`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': 'Bearer lm-studio', | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: testModel, | ||
| messages: [{ role: 'user', content: 'Say "connected" in one word.' }], | ||
| temperature: 0, | ||
| max_tokens: 10, | ||
| stream: false, | ||
| }), | ||
| }); | ||
|
|
||
| if (!res.ok) { | ||
| const text = await res.text().catch(() => ''); | ||
| throw new Error(`HTTP ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}`); | ||
| } | ||
|
|
||
| const json: any = await res.json(); | ||
| const output = json?.choices?.[0]?.message?.content?.trim?.() ?? ''; | ||
| bonsaiLog('Connection test successful. Response:', output); | ||
| panel.webview.postMessage({ command: 'connectionTestResult', success: true, message: `✓ Connected! Model responded: "${output}"` }); | ||
| } catch (err: any) { | ||
| bonsaiLog('Connection test failed:', err?.message || err); | ||
| panel.webview.postMessage({ command: 'connectionTestResult', success: false, message: `✗ Connection failed: ${err?.message || err}` }); | ||
| } | ||
| return; | ||
| } |
There was a problem hiding this comment.
The testConnection handler code in extension.ts (lines 555-596) is nearly identical to the code in server.ts (lines 493-534). This duplication makes maintenance harder and increases the risk of bugs when changes are made to one but not the other. Consider extracting this logic into a shared utility function that both files can use, or at minimum document this duplication so future maintainers are aware they need to update both places.
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.