-
Notifications
You must be signed in to change notification settings - Fork 876
Add opt-in Responses API support for OpenAI-compatible providers #1071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -125,6 +125,18 @@ | |
| "The temperature parameter is not sent. The provider or model default is used.": "The temperature parameter is not sent. The provider or model default is used.", | ||
| "The current model does not accept a custom temperature. The parameter will not be sent.": "The current model does not accept a custom temperature. The parameter will not be sent.", | ||
| "API Url": "API Url", | ||
| "API Protocol": "API Protocol", | ||
| "Chat Completions URL": "Chat Completions URL", | ||
| "Responses URL": "Responses URL", | ||
| "Default protocol": "Default protocol", | ||
| "Use the global OpenAI setting where applicable; otherwise use Chat Completions.": "Use the global OpenAI setting where applicable; otherwise use Chat Completions.", | ||
| "Optional when Responses has an explicit URL.": "Optional when Responses has an explicit URL.", | ||
| "Leave empty to derive from the Chat Completions URL.": "Leave empty to derive from the Chat Completions URL.", | ||
|
Comment on lines
+132
to
+134
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Two english entries exceed 100 columns The localization entries at lines 132 and 134 place each English key and its identical value on one physical line longer than 100 characters. Width-based source checks encounter lengths of 166 and 114 characters when processing the newly added protocol guidance. Agent Prompt
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These entries follow the repository's canonical Prettier JSON formatting. printWidth is a printing target, not a hard maximum for every JSON property, and there is no ESLint max-len rule here. Manually separating these keys and string values would be rejoined by the formatter; keeping the canonical output avoids a formatting-only conflict without changing either string. |
||
| "Please enter a valid HTTP(S) Responses URL": "Please enter a valid HTTP(S) Responses URL", | ||
| "Chat Completions": "Chat Completions", | ||
| "Responses": "Responses", | ||
| "OpenAI API Protocol": "OpenAI API Protocol", | ||
| "Use Responses API (Azure preview)": "Use Responses API (Azure preview)", | ||
| "Provider": "Provider", | ||
| "Others": "Others", | ||
| "API Modes": "API Modes", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ import { | |
| applyPendingProviderChanges, | ||
| areProviderIdsEquivalent, | ||
| buildEditedProvider, | ||
| buildProviderDraft, | ||
| createProviderId, | ||
| getApiModeDisplayLabel, | ||
| getConfiguredCustomApiModesForSessionRecovery, | ||
|
|
@@ -34,14 +35,14 @@ import { | |
| resolveEditingProviderSelection, | ||
| resolveEditingProviderIdForGroupChange, | ||
| resolveSelectableProviderId, | ||
| resolveProviderChatEndpointUrl, | ||
| sanitizeApiModeForSave, | ||
| shouldHandleSavedConversationStorageChange, | ||
| shouldIncludeSelectedApiModeInReferenceCheck, | ||
| shouldPersistDeletedProviderChanges, | ||
| shouldPersistPendingProviderChanges, | ||
| shouldRenderApiModeRow, | ||
| validateProviderEndpointDraft, | ||
| validateProviderResponsesEndpointDraft, | ||
| } from './api-modes-provider-utils.mjs' | ||
|
|
||
| ApiModes.propTypes = { | ||
|
|
@@ -62,14 +63,12 @@ const defaultApiMode = { | |
| active: true, | ||
| } | ||
|
|
||
| const defaultProviderDraft = { | ||
| name: '', | ||
| apiUrl: '', | ||
| } | ||
| const defaultProviderDraft = buildProviderDraft() | ||
|
|
||
| const defaultProviderDraftValidation = { | ||
| name: false, | ||
| apiUrl: false, | ||
| responsesUrl: false, | ||
| } | ||
|
|
||
| export function ApiModes({ config, updateConfig }) { | ||
|
|
@@ -95,6 +94,7 @@ export function ApiModes({ config, updateConfig }) { | |
| const [providerSelectionValidation, setProviderSelectionValidation] = useState(false) | ||
| const providerNameInputRef = useRef(null) | ||
| const providerBaseUrlInputRef = useRef(null) | ||
| const providerResponsesUrlInputRef = useRef(null) | ||
| const providerSelectorRef = useRef(null) | ||
|
|
||
| useLayoutEffect(() => { | ||
|
|
@@ -261,10 +261,7 @@ export function ApiModes({ config, updateConfig }) { | |
| event.preventDefault() | ||
| if (!selectedCustomProvider) return | ||
| setProviderEditingId(selectedCustomProvider.id) | ||
| setProviderDraft({ | ||
| name: selectedCustomProvider.name || '', | ||
| apiUrl: resolveProviderChatEndpointUrl(selectedCustomProvider), | ||
| }) | ||
| setProviderDraft(buildProviderDraft(selectedCustomProvider)) | ||
| setProviderDraftValidation(defaultProviderDraftValidation) | ||
| setIsProviderEditorOpen(true) | ||
| } | ||
|
|
@@ -276,18 +273,25 @@ export function ApiModes({ config, updateConfig }) { | |
| pendingNewProvider && pendingNewProvider.id === providerEditingId | ||
| ? pendingNewProvider | ||
| : selectedCustomProvider || {} | ||
| const endpointDraft = validateProviderEndpointDraft(providerDraft.apiUrl) | ||
| const endpointDraft = validateProviderEndpointDraft(providerDraft.apiUrl, providerDraft) | ||
| const responsesEndpointDraft = validateProviderResponsesEndpointDraft( | ||
| providerDraft, | ||
| providerEditingId ? existingProvider : undefined, | ||
| ) | ||
| const parsedEndpoint = endpointDraft.parsedEndpoint | ||
| const nextProviderDraftValidation = { | ||
| name: !providerName, | ||
| apiUrl: !endpointDraft.valid, | ||
| responsesUrl: !responsesEndpointDraft.valid, | ||
| } | ||
| if (nextProviderDraftValidation.name || nextProviderDraftValidation.apiUrl) { | ||
| if (Object.values(nextProviderDraftValidation).some(Boolean)) { | ||
| setProviderDraftValidation(nextProviderDraftValidation) | ||
| if (nextProviderDraftValidation.name) { | ||
| providerNameInputRef.current?.focus() | ||
| } else { | ||
| } else if (nextProviderDraftValidation.apiUrl) { | ||
| providerBaseUrlInputRef.current?.focus() | ||
| } else { | ||
| providerResponsesUrlInputRef.current?.focus() | ||
| } | ||
| return | ||
| } | ||
|
|
@@ -299,6 +303,7 @@ export function ApiModes({ config, updateConfig }) { | |
| providerName, | ||
| parsedEndpoint, | ||
| providerDraft.apiUrl, | ||
| providerDraft, | ||
| ) | ||
| : null | ||
|
|
||
|
|
@@ -319,17 +324,20 @@ export function ApiModes({ config, updateConfig }) { | |
| ...Object.values(OPENAI_COMPATIBLE_GROUP_TO_PROVIDER_ID), | ||
| ...pendingDeletedProviderIds, | ||
| ]) | ||
| const createdProvider = { | ||
| id: providerId, | ||
| name: providerName, | ||
| baseUrl: '', | ||
| chatCompletionsPath: '/v1/chat/completions', | ||
| completionsPath: '/v1/completions', | ||
| chatCompletionsUrl: parsedEndpoint.chatCompletionsUrl, | ||
| completionsUrl: parsedEndpoint.completionsUrl, | ||
| enabled: true, | ||
| allowLegacyResponseField: true, | ||
| } | ||
| const createdProvider = buildEditedProvider( | ||
| { | ||
| baseUrl: '', | ||
| chatCompletionsPath: '/v1/chat/completions', | ||
| completionsPath: '/v1/completions', | ||
| enabled: true, | ||
| allowLegacyResponseField: true, | ||
| }, | ||
| providerId, | ||
| providerName, | ||
| parsedEndpoint, | ||
| providerDraft.apiUrl, | ||
| providerDraft, | ||
| ) | ||
| setPendingNewProvider(createdProvider) | ||
| setProviderSelector(providerId) | ||
| setProviderSelectionValidation(false) | ||
|
|
@@ -550,27 +558,71 @@ export function ApiModes({ config, updateConfig }) { | |
| aria-invalid={providerDraftValidation.name} | ||
| style={providerDraftValidation.name ? { borderColor: 'red' } : undefined} | ||
| /> | ||
| <input | ||
| type="text" | ||
| ref={providerBaseUrlInputRef} | ||
| value={providerDraft.apiUrl} | ||
| placeholder="https://api.example.com/v1/chat/completions" | ||
| title={t('API Url')} | ||
| onChange={(e) => { | ||
| setProviderDraft({ ...providerDraft, apiUrl: e.target.value }) | ||
| if (providerDraftValidation.apiUrl) { | ||
| setProviderDraftValidation({ | ||
| ...providerDraftValidation, | ||
| apiUrl: false, | ||
| }) | ||
| } | ||
| }} | ||
| aria-invalid={providerDraftValidation.apiUrl} | ||
| style={providerDraftValidation.apiUrl ? { borderColor: 'red' } : undefined} | ||
| /> | ||
| <label> | ||
| {t('Chat Completions URL')} | ||
| <input | ||
| type="text" | ||
| ref={providerBaseUrlInputRef} | ||
| value={providerDraft.apiUrl} | ||
| placeholder="https://api.example.com/v1/chat/completions" | ||
|
Comment on lines
+564
to
+567
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Provider editor uses double quotes ApiModes, ApiUrl, and GeneralPart add double-quoted JSX literals for endpoint type and placeholder attributes, protocol option value attributes, and the Azure Responses checkbox type attribute. Edits that copy these controls or add adjacent endpoint, protocol, or provider settings can propagate the inconsistent quoting pattern throughout the provider editor. Agent Prompt
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JSX attributes use double quotes under the existing Prettier configuration. |
||
| title={t('Chat Completions URL')} | ||
| onChange={(e) => { | ||
| setProviderDraft({ ...providerDraft, apiUrl: e.target.value }) | ||
| if (providerDraftValidation.apiUrl) { | ||
| setProviderDraftValidation({ | ||
| ...providerDraftValidation, | ||
| apiUrl: false, | ||
| }) | ||
| } | ||
| }} | ||
| aria-invalid={providerDraftValidation.apiUrl} | ||
| style={providerDraftValidation.apiUrl ? { borderColor: 'red' } : undefined} | ||
| /> | ||
| </label> | ||
| <small>{t('Optional when Responses has an explicit URL.')}</small> | ||
| {providerDraftValidation.apiUrl && ( | ||
| <div style={{ color: 'red' }}>{t('Please enter a full Chat Completions URL')}</div> | ||
| )} | ||
| <label> | ||
| {t('Responses URL')} | ||
| <input | ||
| type="text" | ||
| ref={providerResponsesUrlInputRef} | ||
| value={providerDraft.responsesUrl} | ||
| placeholder="https://api.example.com/v1/responses" | ||
| title={t('Responses URL')} | ||
| onChange={(e) => { | ||
| setProviderDraft({ ...providerDraft, responsesUrl: e.target.value }) | ||
| if (providerDraftValidation.responsesUrl) { | ||
| setProviderDraftValidation({ ...providerDraftValidation, responsesUrl: false }) | ||
| } | ||
| }} | ||
| aria-invalid={providerDraftValidation.responsesUrl} | ||
| style={providerDraftValidation.responsesUrl ? { borderColor: 'red' } : undefined} | ||
| /> | ||
| </label> | ||
| <small>{t('Leave empty to derive from the Chat Completions URL.')}</small> | ||
| {providerDraftValidation.responsesUrl && ( | ||
| <div style={{ color: 'red' }}>{t('Please enter a valid HTTP(S) Responses URL')}</div> | ||
| )} | ||
| <label style={{ display: 'flex', gap: '4px', alignItems: 'center' }}> | ||
| {t('API Protocol')} | ||
| <select | ||
| value={providerDraft.apiProtocol} | ||
| onChange={(e) => { | ||
| setProviderDraft({ ...providerDraft, apiProtocol: e.target.value }) | ||
| }} | ||
| > | ||
| <option value="default">{t('Default protocol')}</option> | ||
| <option value="chat">{t('Chat Completions')}</option> | ||
| <option value="responses">{t('Responses')}</option> | ||
| </select> | ||
| </label> | ||
| {providerDraft.apiProtocol === 'default' && ( | ||
| <small> | ||
| {t('Use the global OpenAI setting where applicable; otherwise use Chat Completions.')} | ||
| </small> | ||
| )} | ||
| <div | ||
| style={{ | ||
| display: 'grid', | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3. Localized users see english controls
📘 Rule violation⚙ MaintainabilityAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools