gitlab-ai-provider 6.14.0 → 6.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +14 -3
- package/dist/gitlab-ai-provider-6.15.1.tgz +0 -0
- package/dist/index.d.mts +40 -13
- package/dist/index.d.ts +40 -13
- package/dist/index.js +90 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +90 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/gitlab-ai-provider-6.14.0.tgz +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1532,6 +1532,10 @@ var MODEL_MAPPINGS = {
|
|
|
1532
1532
|
"duo-chat-opus-4-5": { provider: "anthropic", model: "claude-opus-4-5-20251101" },
|
|
1533
1533
|
"duo-chat-sonnet-4-5": { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
|
|
1534
1534
|
"duo-chat-haiku-4-5": { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
|
|
1535
|
+
// OpenAI models - Responses API
|
|
1536
|
+
// GPT-6 Astra rejects `reasoning_effort` with function tools on Chat Completions;
|
|
1537
|
+
// it must use the Responses API (matches OpenAI's own routing for GPT-5.x/GPT-6).
|
|
1538
|
+
"duo-chat-gpt-6-astra": { provider: "openai", model: "gpt-6-astra", openaiApiType: "responses" },
|
|
1535
1539
|
// OpenAI models - Chat Completions API
|
|
1536
1540
|
"duo-chat-gpt-5-1": { provider: "openai", model: "gpt-5.1-2025-11-13", openaiApiType: "chat" },
|
|
1537
1541
|
"duo-chat-gpt-5-2": { provider: "openai", model: "gpt-5.2-2025-12-11", openaiApiType: "chat" },
|
|
@@ -1579,14 +1583,14 @@ var MODEL_MAPPINGS = {
|
|
|
1579
1583
|
"duo-workflow-default": { provider: "workflow", model: "default" },
|
|
1580
1584
|
"duo-workflow-sonnet-4-5": {
|
|
1581
1585
|
provider: "workflow",
|
|
1582
|
-
model: "
|
|
1586
|
+
model: "claude_sonnet_4_5_20250929"
|
|
1583
1587
|
},
|
|
1584
1588
|
"duo-workflow-sonnet-5": { provider: "workflow", model: "claude_sonnet_5" },
|
|
1585
1589
|
"duo-workflow-opus-5": { provider: "workflow", model: "claude_opus_5" },
|
|
1586
1590
|
"duo-workflow-sonnet-4-6": { provider: "workflow", model: "claude_sonnet_4_6" },
|
|
1587
1591
|
"duo-workflow-opus-4-5": {
|
|
1588
1592
|
provider: "workflow",
|
|
1589
|
-
model: "
|
|
1593
|
+
model: "claude_opus_4_5_20251101"
|
|
1590
1594
|
},
|
|
1591
1595
|
"duo-workflow-haiku-4-5": { provider: "workflow", model: "claude_haiku_4_5_20251001" },
|
|
1592
1596
|
"duo-workflow-opus-4-6": { provider: "workflow", model: "claude_opus_4_6_20260205" }
|
|
@@ -2688,7 +2692,7 @@ import { AsyncResource } from "async_hooks";
|
|
|
2688
2692
|
import WebSocket from "isomorphic-ws";
|
|
2689
2693
|
|
|
2690
2694
|
// src/version.ts
|
|
2691
|
-
var VERSION = true ? "6.
|
|
2695
|
+
var VERSION = true ? "6.15.0" : "0.0.0-dev";
|
|
2692
2696
|
|
|
2693
2697
|
// src/gitlab-workflow-client.ts
|
|
2694
2698
|
var WS_CONNECT_TIMEOUT_MS = 3e4;
|
|
@@ -3408,8 +3412,14 @@ var GitLabProjectDetector = class {
|
|
|
3408
3412
|
};
|
|
3409
3413
|
|
|
3410
3414
|
// src/gitlab-model-discovery.ts
|
|
3415
|
+
function scopeCacheKey(scope) {
|
|
3416
|
+
return "projectId" in scope ? `p:${scope.projectId}` : `n:${scope.rootNamespaceId}`;
|
|
3417
|
+
}
|
|
3418
|
+
function normalizeScope(scope) {
|
|
3419
|
+
return typeof scope === "string" ? { rootNamespaceId: scope } : scope;
|
|
3420
|
+
}
|
|
3411
3421
|
var AI_CHAT_AVAILABLE_MODELS_QUERY = `
|
|
3412
|
-
query aiChatAvailableModels($rootNamespaceId: GroupID
|
|
3422
|
+
query aiChatAvailableModels($projectId: ProjectID, $rootNamespaceId: GroupID) {
|
|
3413
3423
|
metadata {
|
|
3414
3424
|
featureFlags(names: ["ai_user_model_switching"]) {
|
|
3415
3425
|
enabled
|
|
@@ -3418,7 +3428,7 @@ var AI_CHAT_AVAILABLE_MODELS_QUERY = `
|
|
|
3418
3428
|
version
|
|
3419
3429
|
}
|
|
3420
3430
|
|
|
3421
|
-
aiChatAvailableModels(rootNamespaceId: $rootNamespaceId) {
|
|
3431
|
+
aiChatAvailableModels(projectId: $projectId, rootNamespaceId: $rootNamespaceId) {
|
|
3422
3432
|
defaultModel {
|
|
3423
3433
|
name
|
|
3424
3434
|
ref
|
|
@@ -3444,18 +3454,42 @@ var GitLabModelDiscovery = class {
|
|
|
3444
3454
|
this.fetchFn = config.fetch ?? fetch;
|
|
3445
3455
|
}
|
|
3446
3456
|
/**
|
|
3447
|
-
* Discover available models for a given root namespace.
|
|
3457
|
+
* Discover available models for a given project or root namespace.
|
|
3448
3458
|
*
|
|
3449
|
-
* Results are cached per
|
|
3459
|
+
* Results are cached per scope with a 10-minute TTL.
|
|
3450
3460
|
* Use `invalidateCache()` to force an immediate refresh.
|
|
3451
3461
|
*
|
|
3452
|
-
* @param
|
|
3462
|
+
* @param scope - `{ projectId }` (preferred), `{ rootNamespaceId }`, or a
|
|
3463
|
+
* bare string (treated as `rootNamespaceId` for backwards compatibility)
|
|
3453
3464
|
*/
|
|
3454
|
-
async discover(
|
|
3455
|
-
const
|
|
3465
|
+
async discover(scope) {
|
|
3466
|
+
const normalized = normalizeScope(scope);
|
|
3467
|
+
const cacheKey = scopeCacheKey(normalized);
|
|
3468
|
+
const cached = this.cache.get(cacheKey);
|
|
3456
3469
|
if (cached && cached.expiresAt > Date.now()) {
|
|
3457
3470
|
return cached.data;
|
|
3458
3471
|
}
|
|
3472
|
+
try {
|
|
3473
|
+
const result = await this.query(normalized);
|
|
3474
|
+
this.cache.set(cacheKey, {
|
|
3475
|
+
data: result,
|
|
3476
|
+
expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
|
|
3477
|
+
});
|
|
3478
|
+
return result;
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
if ("projectId" in normalized && normalized.rootNamespaceId) {
|
|
3481
|
+
const fallbackScope = { rootNamespaceId: normalized.rootNamespaceId };
|
|
3482
|
+
const result = await this.query(fallbackScope);
|
|
3483
|
+
this.cache.set(scopeCacheKey(fallbackScope), {
|
|
3484
|
+
data: result,
|
|
3485
|
+
expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
|
|
3486
|
+
});
|
|
3487
|
+
return result;
|
|
3488
|
+
}
|
|
3489
|
+
throw error;
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
async query(scope) {
|
|
3459
3493
|
const url = `${this.config.instanceUrl}/api/graphql`;
|
|
3460
3494
|
try {
|
|
3461
3495
|
const response = await this.fetchFn(url, {
|
|
@@ -3466,7 +3500,7 @@ var GitLabModelDiscovery = class {
|
|
|
3466
3500
|
},
|
|
3467
3501
|
body: JSON.stringify({
|
|
3468
3502
|
query: AI_CHAT_AVAILABLE_MODELS_QUERY,
|
|
3469
|
-
variables: { rootNamespaceId }
|
|
3503
|
+
variables: "projectId" in scope ? { projectId: scope.projectId, rootNamespaceId: void 0 } : { projectId: void 0, rootNamespaceId: scope.rootNamespaceId }
|
|
3470
3504
|
})
|
|
3471
3505
|
});
|
|
3472
3506
|
if (!response.ok) {
|
|
@@ -3485,19 +3519,15 @@ var GitLabModelDiscovery = class {
|
|
|
3485
3519
|
}
|
|
3486
3520
|
const models = json.data?.aiChatAvailableModels;
|
|
3487
3521
|
const metadata = json.data?.metadata;
|
|
3488
|
-
const
|
|
3489
|
-
const
|
|
3522
|
+
const flag = metadata?.featureFlags?.find((f) => f.name === "ai_user_model_switching");
|
|
3523
|
+
const modelSwitchingEnabled = flag ? flag.enabled : true;
|
|
3524
|
+
return {
|
|
3490
3525
|
defaultModel: models?.defaultModel ?? null,
|
|
3491
3526
|
selectableModels: models?.selectableModels ?? [],
|
|
3492
3527
|
pinnedModel: models?.pinnedModel ?? null,
|
|
3493
3528
|
modelSwitchingEnabled,
|
|
3494
3529
|
instanceVersion: metadata?.version ?? null
|
|
3495
3530
|
};
|
|
3496
|
-
this.cache.set(rootNamespaceId, {
|
|
3497
|
-
data: result,
|
|
3498
|
-
expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
|
|
3499
|
-
});
|
|
3500
|
-
return result;
|
|
3501
3531
|
} catch (error) {
|
|
3502
3532
|
if (error instanceof GitLabError) throw error;
|
|
3503
3533
|
throw new GitLabError({
|
|
@@ -3511,11 +3541,11 @@ var GitLabModelDiscovery = class {
|
|
|
3511
3541
|
*
|
|
3512
3542
|
* Priority: pinned > user-selected > default.
|
|
3513
3543
|
*
|
|
3514
|
-
* @param
|
|
3544
|
+
* @param scope - Project or root namespace to discover models for
|
|
3515
3545
|
* @param userSelectedRef - Optional user preference
|
|
3516
3546
|
*/
|
|
3517
|
-
async getEffectiveModelRef(
|
|
3518
|
-
const discovered = await this.discover(
|
|
3547
|
+
async getEffectiveModelRef(scope, userSelectedRef) {
|
|
3548
|
+
const discovered = await this.discover(scope);
|
|
3519
3549
|
if (discovered.pinnedModel) {
|
|
3520
3550
|
return discovered.pinnedModel.ref;
|
|
3521
3551
|
}
|
|
@@ -3962,6 +3992,25 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3962
3992
|
this._rootNamespaceId = null;
|
|
3963
3993
|
return null;
|
|
3964
3994
|
}
|
|
3995
|
+
/**
|
|
3996
|
+
* Resolve the model discovery scope to use for `aiChatAvailableModels`.
|
|
3997
|
+
*
|
|
3998
|
+
* `projectId` is preferred — it works for both group- and user-namespace
|
|
3999
|
+
* projects — with `rootNamespaceId` attached as a fallback for GitLab
|
|
4000
|
+
* instances too old to support `projectId`-scoped discovery.
|
|
4001
|
+
*/
|
|
4002
|
+
async resolveDiscoveryScope() {
|
|
4003
|
+
const rootNamespaceId = await this.resolveRootNamespaceId();
|
|
4004
|
+
const workDir = this.workflowOptions.workingDirectory ?? process.cwd();
|
|
4005
|
+
const project = await this.projectDetector.detectProject(workDir);
|
|
4006
|
+
if (project?.id) {
|
|
4007
|
+
return {
|
|
4008
|
+
projectId: `gid://gitlab/Project/${project.id}`,
|
|
4009
|
+
...rootNamespaceId ? { rootNamespaceId } : {}
|
|
4010
|
+
};
|
|
4011
|
+
}
|
|
4012
|
+
return rootNamespaceId ? { rootNamespaceId } : null;
|
|
4013
|
+
}
|
|
3965
4014
|
/**
|
|
3966
4015
|
* Resolve the effective DWS model ref to use for this stream.
|
|
3967
4016
|
* Deduplicates concurrent calls via a shared promise.
|
|
@@ -3992,13 +4041,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3992
4041
|
return this._discoveryPromise;
|
|
3993
4042
|
}
|
|
3994
4043
|
async doResolveModelRef() {
|
|
3995
|
-
const
|
|
3996
|
-
if (!
|
|
4044
|
+
const scope = await this.resolveDiscoveryScope();
|
|
4045
|
+
if (!scope) {
|
|
3997
4046
|
this._selectedModelRef = "default";
|
|
3998
4047
|
return "default";
|
|
3999
4048
|
}
|
|
4000
4049
|
try {
|
|
4001
|
-
const discovered = await this.modelDiscovery.discover(
|
|
4050
|
+
const discovered = await this.modelDiscovery.discover(scope);
|
|
4002
4051
|
this.modelCache.saveDiscovery(discovered);
|
|
4003
4052
|
if (discovered.pinnedModel) {
|
|
4004
4053
|
this._selectedModelRef = discovered.pinnedModel.ref;
|
|
@@ -4041,11 +4090,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
4041
4090
|
* Call this early (e.g., on IDE startup) to avoid blocking the first stream.
|
|
4042
4091
|
* Results are persisted to the workspace model cache.
|
|
4043
4092
|
*
|
|
4044
|
-
* @param
|
|
4093
|
+
* @param scope - Project or root namespace GID to discover models for.
|
|
4094
|
+
* A bare string is treated as a `rootNamespaceId` (e.g.
|
|
4095
|
+
* 'gid://gitlab/Group/12345') for backwards compatibility.
|
|
4045
4096
|
* @returns Discovered models with default, selectable, and pinned models
|
|
4046
4097
|
*/
|
|
4047
|
-
async discoverModels(
|
|
4048
|
-
const result = await this.modelDiscovery.discover(
|
|
4098
|
+
async discoverModels(scope) {
|
|
4099
|
+
const result = await this.modelDiscovery.discover(scope);
|
|
4049
4100
|
this.modelCache.saveDiscovery(result);
|
|
4050
4101
|
return result;
|
|
4051
4102
|
}
|
|
@@ -5787,7 +5838,7 @@ async function buildModels(discovered, project) {
|
|
|
5787
5838
|
for (const model of all) {
|
|
5788
5839
|
if (!model.ref || seen.has(model.ref)) continue;
|
|
5789
5840
|
seen.add(model.ref);
|
|
5790
|
-
const id = refToID.get(model.ref) ?? `duo-workflow-${model.ref.replace(/[/_]/g, "-")}
|
|
5841
|
+
const id = refToID.get(model.ref) ?? (model.ref.startsWith("__default__") ? "duo-workflow-default" : `duo-workflow-${model.ref.replace(/[/_]/g, "-")}`);
|
|
5791
5842
|
const limits = configs.get(model.ref);
|
|
5792
5843
|
models.push({
|
|
5793
5844
|
id,
|
|
@@ -5819,14 +5870,22 @@ async function discoverWorkflowModels(config, options) {
|
|
|
5819
5870
|
} catch {
|
|
5820
5871
|
return { models: [], project: null };
|
|
5821
5872
|
}
|
|
5822
|
-
|
|
5823
|
-
|
|
5873
|
+
if (!project?.id) return { models: [], project };
|
|
5874
|
+
const scope = {
|
|
5875
|
+
projectId: `gid://gitlab/Project/${project.id}`,
|
|
5876
|
+
...project.namespaceId ? { rootNamespaceId: `gid://gitlab/Group/${project.namespaceId}` } : {}
|
|
5877
|
+
};
|
|
5824
5878
|
const discovery = new GitLabModelDiscovery({
|
|
5825
5879
|
instanceUrl: config.instanceUrl,
|
|
5826
5880
|
getHeaders: config.getHeaders,
|
|
5827
5881
|
fetch: config.fetch
|
|
5828
5882
|
});
|
|
5829
|
-
|
|
5883
|
+
let discovered;
|
|
5884
|
+
try {
|
|
5885
|
+
discovered = await discovery.discover(scope);
|
|
5886
|
+
} catch {
|
|
5887
|
+
return { models: [], project };
|
|
5888
|
+
}
|
|
5830
5889
|
cache.saveDiscovery(discovered, project);
|
|
5831
5890
|
if (!cache.getSelectedModelRef()) {
|
|
5832
5891
|
const pick = discovered.pinnedModel ?? discovered.defaultModel;
|