Skip to content

Commit f96d38e

Browse files
Lymah123zanesq
andauthored
Desktop: - No Custom Headers field for custom OpenAI-compatible providers (#6681)
Signed-off-by: Lymah123 <fimihanodunola625@gmail.com> Co-authored-by: Zane Staggs <zane@squareup.com>
1 parent 153872a commit f96d38e

7 files changed

Lines changed: 246 additions & 25 deletions

File tree

crates/goose/src/config/declarative_providers.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@ pub fn update_custom_provider(params: UpdateCustomProviderParams) -> Result<()>
207207
api_key_env,
208208
base_url: params.api_url,
209209
models: model_infos,
210-
headers: params.headers.or(existing_config.headers),
210+
headers: match params.headers {
211+
Some(h) if h.is_empty() => None,
212+
Some(h) => Some(h),
213+
None => existing_config.headers,
214+
},
211215
timeout_seconds: existing_config.timeout_seconds,
212216
supports_streaming: params.supports_streaming,
213217
requires_auth: params.requires_auth,

ui/desktop/src/components/ChatInput.tsx

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export default function ChatInput({
126126
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
127127
const [isFocused, setIsFocused] = useState(false);
128128
const [pastedImages, setPastedImages] = useState<PastedImage[]>([]);
129+
const [isFilePickerOpen, setIsFilePickerOpen] = useState(false);
129130

130131
// Derived state - chatState != Idle means we're in some form of loading state
131132
const isLoading = chatState !== ChatState.Idle;
@@ -148,7 +149,6 @@ export default function ChatInput({
148149
const [diagnosticsOpen, setDiagnosticsOpen] = useState(false);
149150
const [showCreateRecipeModal, setShowCreateRecipeModal] = useState(false);
150151
const [showEditRecipeModal, setShowEditRecipeModal] = useState(false);
151-
const [isFilePickerOpen, setIsFilePickerOpen] = useState(false);
152152
const [sessionWorkingDir, setSessionWorkingDir] = useState<string | null>(null);
153153

154154
useEffect(() => {
@@ -1190,13 +1190,11 @@ export default function ChatInput({
11901190

11911191
return (
11921192
<div
1193-
className={`flex flex-col relative h-auto p-4 transition-colors ${
1194-
disableAnimation ? '' : 'page-transition'
1195-
} ${
1196-
isFocused
1193+
className={`flex flex-col relative h-auto p-4 transition-colors ${disableAnimation ? '' : 'page-transition'
1194+
} ${isFocused
11971195
? 'border-border-strong hover:border-border-strong'
11981196
: 'border-border-default hover:border-border-default'
1199-
} bg-background-default z-10 rounded-t-2xl`}
1197+
} bg-background-default z-10 rounded-t-2xl`}
12001198
data-drop-zone="true"
12011199
onDrop={handleLocalDrop}
12021200
onDragOver={handleLocalDragOver}
@@ -1265,7 +1263,7 @@ export default function ChatInput({
12651263
size="sm"
12661264
shape="round"
12671265
variant="outline"
1268-
onClick={() => {}}
1266+
onClick={() => { }}
12691267
disabled={true}
12701268
className="bg-slate-600 text-white cursor-not-allowed opacity-50 border-slate-600 rounded-full px-6 py-2"
12711269
>
@@ -1312,13 +1310,12 @@ export default function ChatInput({
13121310
}
13131311
}}
13141312
disabled={isTranscribing}
1315-
className={`rounded-full px-6 py-2 ${
1316-
isRecording
1317-
? 'bg-red-500 text-white hover:bg-red-600 border-red-500'
1318-
: isTranscribing
1319-
? 'bg-slate-600 text-white cursor-not-allowed animate-pulse border-slate-600'
1320-
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600'
1321-
}`}
1313+
className={`rounded-full px-6 py-2 ${isRecording
1314+
? 'bg-red-500 text-white hover:bg-red-600 border-red-500'
1315+
: isTranscribing
1316+
? 'bg-slate-600 text-white cursor-not-allowed animate-pulse border-slate-600'
1317+
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600'
1318+
}`}
13221319
>
13231320
<Microphone />
13241321
</Button>
@@ -1356,11 +1353,10 @@ export default function ChatInput({
13561353
shape="round"
13571354
variant="outline"
13581355
disabled={isSubmitButtonDisabled}
1359-
className={`rounded-full px-10 py-2 flex items-center gap-2 ${
1360-
isSubmitButtonDisabled
1361-
? 'bg-slate-600 text-white cursor-not-allowed opacity-50 border-slate-600'
1362-
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600 hover:cursor-pointer'
1363-
}`}
1356+
className={`rounded-full px-10 py-2 flex items-center gap-2 ${isSubmitButtonDisabled
1357+
? 'bg-slate-600 text-white cursor-not-allowed opacity-50 border-slate-600'
1358+
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600 hover:cursor-pointer'
1359+
}`}
13641360
>
13651361
<Send className="w-4 h-4" />
13661362
<span className="text-sm">Send</span>

ui/desktop/src/components/settings/extensions/modal/ExtensionModal.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,19 @@ export default function ExtensionModal({
156156
};
157157

158158
const handleHeaderChange = (index: number, field: 'key' | 'value', value: string) => {
159+
if (field === 'key') {
160+
if (value.includes(' ')) {
161+
return;
162+
}
163+
const trimmedNewKey = value.trim();
164+
const normalizedNewKey = trimmedNewKey.toLowerCase();
165+
const isDuplicate = formData.headers.some(
166+
(h, i) => i !== index && h.key.trim().toLowerCase() === normalizedNewKey,
167+
);
168+
if (isDuplicate && trimmedNewKey !== '') {
169+
return;
170+
}
171+
}
159172
const newHeaders = [...formData.headers];
160173
newHeaders[index][field] = value;
161174

ui/desktop/src/components/settings/extensions/modal/HeadersSection.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export default function HeadersSection({
4444
const keyEmpty = !newKey.trim();
4545
const valueEmpty = !newValue.trim();
4646
const keyHasSpaces = newKey.includes(' ');
47+
const normalizedNewKey = newKey.trim().toLowerCase();
48+
const isDuplicate = headers.some(
49+
h => h.key.trim().toLowerCase() === normalizedNewKey
50+
);
4751

4852
if (keyEmpty || valueEmpty) {
4953
setInvalidFields({
@@ -63,6 +67,15 @@ export default function HeadersSection({
6367
return;
6468
}
6569

70+
if (isDuplicate) {
71+
setInvalidFields({
72+
key: true,
73+
value: false,
74+
});
75+
setValidationError('A header with this name already exists');
76+
return;
77+
}
78+
6679
setValidationError(null);
6780
setInvalidFields({ key: false, value: false });
6881
onAdd(newKey, newValue);

ui/desktop/src/components/settings/extensions/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ export function extensionToFormData(extension: FixedExtensionEntry): ExtensionFo
100100
description: extension.description || '',
101101
type:
102102
extension.type === 'frontend' ||
103-
extension.type === 'inline_python' ||
104-
extension.type === 'platform'
103+
extension.type === 'inline_python' ||
104+
extension.type === 'platform'
105105
? 'stdio'
106106
: extension.type,
107107
cmd: extension.type === 'stdio' ? quoteShell([extension.cmd, ...extension.args]) : undefined,
@@ -155,7 +155,7 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
155155
timeout: formData.timeout,
156156
uri: formData.endpoint || '',
157157
...(env_keys.length > 0 ? { env_keys } : {}),
158-
...(Object.keys(headers).length > 0 ? { headers } : {}),
158+
headers,
159159
};
160160
} else {
161161
// For other types

ui/desktop/src/components/settings/providers/ProviderGrid.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ function ProviderCards({
238238
models: editingProvider.config.models.map((m) => m.name),
239239
supports_streaming: editingProvider.config.supports_streaming ?? true,
240240
requires_auth: editingProvider.config.requires_auth ?? true,
241+
headers: editingProvider.config.headers ?? undefined,
241242
};
242243

243244
const editable = editingProvider ? editingProvider.isEditable : true;
@@ -246,7 +247,7 @@ function ProviderCards({
246247
<>
247248
{providerCards}
248249
<Dialog open={showCustomProviderModal} onOpenChange={handleCloseModal}>
249-
<DialogContent className="sm:max-w-[600px]">
250+
<DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
250251
<DialogHeader>
251252
<DialogTitle>{title}</DialogTitle>
252253
</DialogHeader>

0 commit comments

Comments
 (0)