-
-
Notifications
You must be signed in to change notification settings - Fork 54
feat: Add OpenAI-compatible API support to AI Writer operation #250
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: main
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 | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,18 +6,43 @@ import { Provider } from './Provider'; | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export class OpenAi extends Provider { | ||||||||||||||||||||||||||||
| constructor(options: AiWriterOperationOptions) { | ||||||||||||||||||||||||||||
| if (!options.apiKeyOpenAi) { | ||||||||||||||||||||||||||||
| throw new InvalidPayloadError({ reason: 'OpenAI API Key is missing' }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| // Determine if this is a custom OpenAI-compatible provider or standard OpenAI | ||||||||||||||||||||||||||||
| const isCustomProvider = options.aiProvider === 'openai-compatible'; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
| const isCustomProvider = options.aiProvider === 'openai-compatible'; | |
| const isCustomProvider = options.aiProvider?.toLowerCase() === 'openai-compatible'; |
Copilot
AI
Mar 10, 2026
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.
The endpoint normalization can produce a duplicated path when the user provides a full /chat/completions/ URL with a trailing slash (e.g., .../chat/completions/ becomes .../chat/completions/chat/completions). Consider trimming trailing slashes before the endsWith('/chat/completions') check (or using new URL() path joining) to make this robust.
| const endpoint = options.customEndpoint.endsWith('/chat/completions') | |
| ? options.customEndpoint | |
| : `${options.customEndpoint.replace(/\/$/, '')}/chat/completions`; | |
| const baseEndpoint = options.customEndpoint.replace(/\/+$/, ''); | |
| const endpoint = baseEndpoint.endsWith('/chat/completions') | |
| ? baseEndpoint | |
| : `${baseEndpoint}/chat/completions`; |
Copilot
AI
Mar 10, 2026
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.
When model is set to 'custom' but customModelName is missing/empty, the request will be sent with model: 'custom'. Add server-side validation to require a non-empty customModelName when model === 'custom' (don’t rely on the app UI’s conditional required field).
| // Use custom model name if provided, otherwise use the selected model | |
| const modelName = this.options.model === 'custom' && this.options.customModelName | |
| ? this.options.customModelName | |
| // Validate that a custom model name is provided when using a custom model | |
| if (this.options.model === 'custom' && !this.options.customModelName) { | |
| throw new InvalidPayloadError({ | |
| reason: 'Custom Model Name is missing', | |
| }); | |
| } | |
| // Use custom model name if provided, otherwise use the selected model | |
| const modelName = this.options.model === 'custom' && this.options.customModelName | |
| ? this.options.customModelName |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -13,13 +13,13 @@ export function getProvider(options: AiWriterOperationOptions) { | |||||
| return new Anthropic(options); | ||||||
| } | ||||||
|
|
||||||
| if (options.aiProvider.toLowerCase() === 'openai') { | ||||||
| if (options.aiProvider.toLowerCase() === 'openai' || options.aiProvider.toLowerCase() === 'openai-compatible') { | ||||||
| return new OpenAi(options); | ||||||
| } | ||||||
|
|
||||||
| if (options.aiProvider.toLowerCase() === 'replicate') { | ||||||
| return new Replicate(options); | ||||||
| } | ||||||
|
|
||||||
| throw new Error(`Unsoported AI Provider ${options.aiProvider}`); | ||||||
| throw new Error(`Unsupported AI Provider ${options.aiProvider}`); | ||||||
|
||||||
| throw new Error(`Unsupported AI Provider ${options.aiProvider}`); | |
| throw new InvalidPayloadError({ reason: `Unsupported AI Provider: ${options.aiProvider}` }); |
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.
The README implies Azure OpenAI / Vertex AI work directly as “OpenAI-compatible APIs”, but Azure’s endpoints/auth (api-key header + api-version + deployments path) and Vertex AI are not Chat Completions-compatible by default. Please clarify that this works with services that expose the OpenAI Chat Completions-compatible endpoint + Bearer auth (or mention that Azure/Vertex require a compatibility/proxy layer).