Spaces:
Runtime error
Runtime error
Enhances API endpoint handling
Browse filesIntroduces logic for appending or modifying API endpoints
Improves user experience with hints and examples
- README.md +4 -1
- server/index.js +8 -1
- src/components/Settings.jsx +19 -1
- src/index.css +65 -1
README.md
CHANGED
|
@@ -60,5 +60,8 @@ This will concurrently run both the frontend development server and the backend
|
|
| 60 |
Configure the application through the settings panel with:
|
| 61 |
|
| 62 |
- API Endpoint
|
|
|
|
|
|
|
|
|
|
| 63 |
- API Key
|
| 64 |
-
- Model Name (e.g., DeepSeek-R1)
|
|
|
|
| 60 |
Configure the application through the settings panel with:
|
| 61 |
|
| 62 |
- API Endpoint
|
| 63 |
+
- Ends with / → /chat/completions will be appended
|
| 64 |
+
- Ends with # → # will be removed
|
| 65 |
+
- Other cases → /v1/chat/completions will be appended
|
| 66 |
- API Key
|
| 67 |
+
- Model Name (e.g., DeepSeek-R1)
|
server/index.js
CHANGED
|
@@ -23,7 +23,14 @@ app.post('/api/chat', async (req, res) => {
|
|
| 23 |
});
|
| 24 |
|
| 25 |
try {
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
console.log('Calling API endpoint:', apiUrl);
|
| 28 |
|
| 29 |
const response = await fetch(apiUrl, {
|
|
|
|
| 23 |
});
|
| 24 |
|
| 25 |
try {
|
| 26 |
+
let apiUrl;
|
| 27 |
+
if (apiEndpoint.endsWith('#')) {
|
| 28 |
+
apiUrl = apiEndpoint.slice(0, -1);
|
| 29 |
+
} else if (apiEndpoint.endsWith('/')) {
|
| 30 |
+
apiUrl = `${apiEndpoint}chat/completions`;
|
| 31 |
+
} else {
|
| 32 |
+
apiUrl = `${apiEndpoint}/v1/chat/completions`;
|
| 33 |
+
}
|
| 34 |
console.log('Calling API endpoint:', apiUrl);
|
| 35 |
|
| 36 |
const response = await fetch(apiUrl, {
|
src/components/Settings.jsx
CHANGED
|
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
|
| 2 |
|
| 3 |
function Settings({ settings, onSave, setSettings }) {
|
| 4 |
const [formData, setFormData] = useState(settings);
|
|
|
|
| 5 |
|
| 6 |
const handleSubmit = (e) => {
|
| 7 |
e.preventDefault();
|
|
@@ -23,6 +24,23 @@ function Settings({ settings, onSave, setSettings }) {
|
|
| 23 |
})}
|
| 24 |
placeholder="Enter API endpoint"
|
| 25 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
</div>
|
| 27 |
<div className="setting-item">
|
| 28 |
<label>API Key:</label>
|
|
@@ -54,4 +72,4 @@ function Settings({ settings, onSave, setSettings }) {
|
|
| 54 |
);
|
| 55 |
}
|
| 56 |
|
| 57 |
-
export default Settings;
|
|
|
|
| 2 |
|
| 3 |
function Settings({ settings, onSave, setSettings }) {
|
| 4 |
const [formData, setFormData] = useState(settings);
|
| 5 |
+
const [isHintExpanded, setIsHintExpanded] = useState(false);
|
| 6 |
|
| 7 |
const handleSubmit = (e) => {
|
| 8 |
e.preventDefault();
|
|
|
|
| 24 |
})}
|
| 25 |
placeholder="Enter API endpoint"
|
| 26 |
/>
|
| 27 |
+
<div className="setting-hint">
|
| 28 |
+
<button
|
| 29 |
+
type="button"
|
| 30 |
+
className="hint-toggle"
|
| 31 |
+
onClick={() => setIsHintExpanded(!isHintExpanded)}
|
| 32 |
+
>
|
| 33 |
+
{isHintExpanded ? 'Hide' : 'Show'} API Endpoint Format Examples
|
| 34 |
+
</button>
|
| 35 |
+
<div className={`hint-content ${isHintExpanded ? 'expanded' : ''}`}>
|
| 36 |
+
<p>API Endpoint format examples:</p>
|
| 37 |
+
<ul>
|
| 38 |
+
<li>Ends with / → /chat/completions will be appended</li>
|
| 39 |
+
<li>Ends with # → # will be removed</li>
|
| 40 |
+
<li>Other cases → /v1/chat/completions will be appended</li>
|
| 41 |
+
</ul>
|
| 42 |
+
</div>
|
| 43 |
+
</div>
|
| 44 |
</div>
|
| 45 |
<div className="setting-item">
|
| 46 |
<label>API Key:</label>
|
|
|
|
| 72 |
);
|
| 73 |
}
|
| 74 |
|
| 75 |
+
export default Settings;
|
src/index.css
CHANGED
|
@@ -563,4 +563,68 @@ button[type="submit"]:disabled {
|
|
| 563 |
gap: 8px;
|
| 564 |
color: #666;
|
| 565 |
font-size: 14px;
|
| 566 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
gap: 8px;
|
| 564 |
color: #666;
|
| 565 |
font-size: 14px;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
/* Setting hint styles */
|
| 569 |
+
.setting-hint {
|
| 570 |
+
margin-top: 8px;
|
| 571 |
+
padding: 12px;
|
| 572 |
+
background: #f8f9fa;
|
| 573 |
+
border-radius: 6px;
|
| 574 |
+
border: 1px solid #e9ecef;
|
| 575 |
+
font-size: 0.9em;
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
.setting-hint p {
|
| 579 |
+
margin: 0 0 8px 0;
|
| 580 |
+
color: #495057;
|
| 581 |
+
font-weight: 500;
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
+
.setting-hint ul {
|
| 585 |
+
margin: 0;
|
| 586 |
+
padding-left: 20px;
|
| 587 |
+
list-style-type: none;
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
.setting-hint li {
|
| 591 |
+
position: relative;
|
| 592 |
+
padding-left: 1.2em;
|
| 593 |
+
margin-bottom: 4px;
|
| 594 |
+
color: #6c757d;
|
| 595 |
+
}
|
| 596 |
+
|
| 597 |
+
.setting-hint li::before {
|
| 598 |
+
content: "•";
|
| 599 |
+
position: absolute;
|
| 600 |
+
left: 0;
|
| 601 |
+
color: #adb5bd;
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
.hint-toggle {
|
| 605 |
+
width: 100%;
|
| 606 |
+
padding: 8px;
|
| 607 |
+
background: #ffffff;
|
| 608 |
+
border: 1px solid #e9ecef;
|
| 609 |
+
border-radius: 4px;
|
| 610 |
+
cursor: pointer;
|
| 611 |
+
font-size: 0.8em;
|
| 612 |
+
color: #495057;
|
| 613 |
+
text-align: left;
|
| 614 |
+
margin-bottom: 8px;
|
| 615 |
+
transition: all 0.2s ease;
|
| 616 |
+
}
|
| 617 |
+
|
| 618 |
+
.hint-toggle:hover {
|
| 619 |
+
background: #f8f9fa;
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
.hint-content {
|
| 623 |
+
max-height: 0;
|
| 624 |
+
overflow: hidden;
|
| 625 |
+
transition: max-height 0.3s ease-out;
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
.hint-content.expanded {
|
| 629 |
+
max-height: 500px; /* Increased to accommodate more content */
|
| 630 |
+
}
|