Skip to content

Commit 1c04016

Browse files
Adds Open-AI compatible provider (#4270)
Moves/renames existing "OpenAICompatibleProvider" base class to "OpenAICompatibleProviderBase"
1 parent bdeee62 commit 1c04016

18 files changed

+701
-276
lines changed

docs/telemetry-events.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
125125
'input.length': number,
126126
'model.id': string,
127-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
127+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
128128
'model.provider.name': string,
129129
'output.length': number,
130130
'retry.count': number,
@@ -155,7 +155,7 @@
155155
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
156156
'input.length': number,
157157
'model.id': string,
158-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
158+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
159159
'model.provider.name': string,
160160
'output.length': number,
161161
'retry.count': number,
@@ -185,7 +185,7 @@ or
185185
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
186186
'input.length': number,
187187
'model.id': string,
188-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
188+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
189189
'model.provider.name': string,
190190
'output.length': number,
191191
'retry.count': number,
@@ -214,7 +214,7 @@ or
214214
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
215215
'input.length': number,
216216
'model.id': string,
217-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
217+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
218218
'model.provider.name': string,
219219
'output.length': number,
220220
'retry.count': number,
@@ -243,7 +243,7 @@ or
243243
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
244244
'input.length': number,
245245
'model.id': string,
246-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
246+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
247247
'model.provider.name': string,
248248
'output.length': number,
249249
'retry.count': number,
@@ -272,7 +272,7 @@ or
272272
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
273273
'input.length': number,
274274
'model.id': string,
275-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
275+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
276276
'model.provider.name': string,
277277
'output.length': number,
278278
'retry.count': number,
@@ -295,7 +295,7 @@ or
295295
```typescript
296296
{
297297
'model.id': string,
298-
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openrouter' | 'vscode' | 'xai',
298+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'ollama' | 'openai' | 'openaicompatible' | 'openrouter' | 'vscode' | 'xai',
299299
'model.provider.name': string
300300
}
301301
```

package.json

+14-1
Original file line numberDiff line numberDiff line change
@@ -4047,7 +4047,7 @@
40474047
"null"
40484048
],
40494049
"default": null,
4050-
"pattern": "^((anthropic|deepseek|gemini|github|huggingface|ollama|openai|openrouter|xai):([\\w.-:]+)|gitkraken|vscode)$",
4050+
"pattern": "^((anthropic|deepseek|gemini|github|huggingface|ollama|openai|openaicompatible|openrouter|xai):([\\w.-:]+)|gitkraken|vscode)$",
40514051
"markdownDescription": "Specifies the AI provider and model to use for GitLens' AI features. Should be formatted as `provider:model` (e.g. `openai:gpt-4o` or `anthropic:claude-3-5-sonnet-latest`), `gitkraken` for GitKraken AI provided models, or `vscode` for models provided by the VS Code extension API (e.g. Copilot)",
40524052
"scope": "window",
40534053
"order": 10,
@@ -4109,6 +4109,19 @@
41094109
"preview"
41104110
]
41114111
},
4112+
"gitlens.ai.openaicompatible.url": {
4113+
"type": [
4114+
"string",
4115+
"null"
4116+
],
4117+
"default": null,
4118+
"markdownDescription": "Specifies a custom URL to use for access to an OpenAI-compatible model. Azure URLs should be in the following format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/chat/completions?api-version={api-version}",
4119+
"scope": "window",
4120+
"order": 31,
4121+
"tags": [
4122+
"preview"
4123+
]
4124+
},
41124125
"gitlens.ai.largePromptWarningThreshold": {
41134126
"type": "number",
41144127
"default": 20000,

src/config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ interface AIConfig {
250250
readonly openai: {
251251
readonly url: string | null;
252252
};
253+
readonly openaicompatible: {
254+
readonly url: string | null;
255+
};
253256
readonly vscode: {
254257
readonly model: AIProviderAndModel | null;
255258
};

src/constants.ai.ts

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type AIProviders =
99
| 'huggingface'
1010
| 'ollama'
1111
| 'openai'
12+
| 'openaicompatible'
1213
| 'openrouter'
1314
| 'vscode'
1415
| 'xai';
@@ -38,6 +39,13 @@ export const openAIProviderDescriptor: AIProviderDescriptor<'openai'> = {
3839
requiresAccount: true,
3940
requiresUserKey: true,
4041
} as const;
42+
export const openAICompatibleProviderDescriptor: AIProviderDescriptor<'openaicompatible'> = {
43+
id: 'openaicompatible',
44+
name: 'OpenAI-Compatible Provider (Azure, etc.)',
45+
primary: false,
46+
requiresAccount: true,
47+
requiresUserKey: true,
48+
} as const;
4149
export const anthropicProviderDescriptor: AIProviderDescriptor<'anthropic'> = {
4250
id: 'anthropic',
4351
name: 'Anthropic',

src/plus/ai/aiProviderService.ts

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
gitKrakenProviderDescriptor,
1010
huggingFaceProviderDescriptor,
1111
ollamaProviderDescriptor,
12+
openAICompatibleProviderDescriptor,
1213
openAIProviderDescriptor,
1314
openRouterProviderDescriptor,
1415
vscodeProviderDescriptor,
@@ -148,6 +149,16 @@ const supportedAIProviders = new Map<AIProviders, AIProviderDescriptorWithType>(
148149
type: lazy(async () => (await import(/* webpackChunkName: "ai" */ './openaiProvider')).OpenAIProvider),
149150
},
150151
],
152+
[
153+
'openaicompatible',
154+
{
155+
...openAICompatibleProviderDescriptor,
156+
type: lazy(
157+
async () =>
158+
(await import(/* webpackChunkName: "ai" */ './openAICompatibleProvider')).OpenAICompatibleProvider,
159+
),
160+
},
161+
],
151162
[
152163
'ollama',
153164
{

src/plus/ai/anthropicProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Response } from '@env/fetch';
33
import { anthropicProviderDescriptor as provider } from '../../constants.ai';
44
import { AIError, AIErrorReason } from '../../errors';
55
import type { AIActionType, AIModel } from './models/model';
6-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
6+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
77

88
type AnthropicModel = AIModel<typeof provider.id>;
99
const models: AnthropicModel[] = [
@@ -103,7 +103,7 @@ const models: AnthropicModel[] = [
103103
},
104104
];
105105

106-
export class AnthropicProvider extends OpenAICompatibleProvider<typeof provider.id> {
106+
export class AnthropicProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
107107
readonly id = provider.id;
108108
readonly name = provider.name;
109109
protected readonly descriptor = provider;

src/plus/ai/deepSeekProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { deepSeekProviderDescriptor as provider } from '../../constants.ai';
22
import type { AIModel } from './models/model';
3-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
3+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
44

55
type DeepSeekModel = AIModel<typeof provider.id>;
66
const models: DeepSeekModel[] = [
@@ -21,7 +21,7 @@ const models: DeepSeekModel[] = [
2121
},
2222
];
2323

24-
export class DeepSeekProvider extends OpenAICompatibleProvider<typeof provider.id> {
24+
export class DeepSeekProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
2525
readonly id = provider.id;
2626
readonly name = provider.name;
2727
protected readonly descriptor = provider;

src/plus/ai/geminiProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { CancellationToken } from 'vscode';
22
import type { Response } from '@env/fetch';
33
import { geminiProviderDescriptor as provider } from '../../constants.ai';
44
import type { AIActionType, AIModel } from './models/model';
5-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
5+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
66

77
type GeminiModel = AIModel<typeof provider.id>;
88
const models: GeminiModel[] = [
@@ -111,7 +111,7 @@ const models: GeminiModel[] = [
111111
},
112112
];
113113

114-
export class GeminiProvider extends OpenAICompatibleProvider<typeof provider.id> {
114+
export class GeminiProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
115115
readonly id = provider.id;
116116
readonly name = provider.name;
117117
protected readonly descriptor = provider;

src/plus/ai/githubModelsProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { fetch } from '@env/fetch';
33
import { githubProviderDescriptor as provider } from '../../constants.ai';
44
import { AIError, AIErrorReason } from '../../errors';
55
import type { AIActionType, AIModel } from './models/model';
6-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
6+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
77

88
type GitHubModelsModel = AIModel<typeof provider.id>;
99

10-
export class GitHubModelsProvider extends OpenAICompatibleProvider<typeof provider.id> {
10+
export class GitHubModelsProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
1111
readonly id = provider.id;
1212
readonly name = provider.name;
1313
protected readonly descriptor = provider;

src/plus/ai/gitkrakenProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import { debug } from '../../system/decorators/log';
66
import { Logger } from '../../system/logger';
77
import { getLogScope } from '../../system/logger.scope';
88
import type { AIActionType, AIModel } from './models/model';
9-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
9+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
1010
import { ensureAccount } from './utils/-webview/ai.utils';
1111

1212
type GitKrakenModel = AIModel<typeof provider.id>;
1313

14-
export class GitKrakenProvider extends OpenAICompatibleProvider<typeof provider.id> {
14+
export class GitKrakenProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
1515
readonly id = provider.id;
1616
readonly name = provider.name;
1717
protected readonly descriptor = provider;

src/plus/ai/huggingFaceProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { fetch } from '@env/fetch';
22
import { huggingFaceProviderDescriptor as provider } from '../../constants.ai';
33
import type { AIModel } from './models/model';
4-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
4+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
55

66
type HuggingFaceModel = AIModel<typeof provider.id>;
77

8-
export class HuggingFaceProvider extends OpenAICompatibleProvider<typeof provider.id> {
8+
export class HuggingFaceProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
99
readonly id = provider.id;
1010
readonly name = provider.name;
1111
protected readonly descriptor = provider;

src/plus/ai/ollamaProvider.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { ollamaProviderDescriptor as provider } from '../../constants.ai';
44
import { configuration } from '../../system/-webview/configuration';
55
import type { AIActionType, AIModel } from './models/model';
66
import type { AIChatMessage, AIRequestResult } from './models/provider';
7-
import { OpenAICompatibleProvider } from './openAICompatibleProvider';
7+
import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';
88

99
type OllamaModel = AIModel<typeof provider.id>;
1010

1111
const defaultBaseUrl = 'http://localhost:11434';
1212

13-
export class OllamaProvider extends OpenAICompatibleProvider<typeof provider.id> {
13+
export class OllamaProvider extends OpenAICompatibleProviderBase<typeof provider.id> {
1414
readonly id = provider.id;
1515
readonly name = provider.name;
1616
protected readonly descriptor = provider;

0 commit comments

Comments
 (0)