@gitlab/gitlab-ai-provider 3.1.2 → 3.1.3
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 +5 -0
- package/dist/gitlab-gitlab-ai-provider-3.1.3.tgz +0 -0
- package/dist/index.js +146 -114
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +146 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/gitlab-gitlab-ai-provider-3.1.2.tgz +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## <small>3.1.3 (2026-01-21)</small>
|
|
6
|
+
|
|
7
|
+
- Merge branch 'fix/streaming-tool-call-handling' into 'main' ([22279b1](https://gitlab.com/gitlab-org/editor-extensions/gitlab-ai-provider/commit/22279b1))
|
|
8
|
+
- fix: refactor streaming to handle tool calls properly ([88fb513](https://gitlab.com/gitlab-org/editor-extensions/gitlab-ai-provider/commit/88fb513))
|
|
9
|
+
|
|
5
10
|
## <small>3.1.2 (2026-01-18)</small>
|
|
6
11
|
|
|
7
12
|
- fix: removed API tools and added new env variable ([a64ef6d](https://gitlab.com/gitlab-org/editor-extensions/gitlab-ai-provider/commit/a64ef6d))
|
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -192,8 +192,6 @@ var GitLabDirectAccessClient = class {
|
|
|
192
192
|
};
|
|
193
193
|
|
|
194
194
|
// src/gitlab-agentic-language-model.ts
|
|
195
|
-
var debugLog = (..._args) => {
|
|
196
|
-
};
|
|
197
195
|
var GitLabAgenticLanguageModel = class {
|
|
198
196
|
specificationVersion = "v2";
|
|
199
197
|
modelId;
|
|
@@ -222,14 +220,7 @@ var GitLabAgenticLanguageModel = class {
|
|
|
222
220
|
*/
|
|
223
221
|
async getAnthropicClient(forceRefresh = false) {
|
|
224
222
|
const tokenData = await this.directAccessClient.getDirectAccessToken(forceRefresh);
|
|
225
|
-
debugLog("[gitlab-ai-provider] Token headers from GitLab:", tokenData.headers);
|
|
226
|
-
debugLog("[gitlab-ai-provider] Proxy URL:", this.directAccessClient.getAnthropicProxyUrl());
|
|
227
223
|
const { "x-api-key": _removed, ...filteredHeaders } = tokenData.headers;
|
|
228
|
-
if (_removed) {
|
|
229
|
-
debugLog(
|
|
230
|
-
"[gitlab-ai-provider] Filtered out x-api-key from headers (using authToken instead)"
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
224
|
this.anthropicClient = new import_sdk.default({
|
|
234
225
|
apiKey: null,
|
|
235
226
|
authToken: tokenData.token,
|
|
@@ -468,114 +459,155 @@ var GitLabAgenticLanguageModel = class {
|
|
|
468
459
|
const self = this;
|
|
469
460
|
const stream = new ReadableStream({
|
|
470
461
|
start: async (controller) => {
|
|
462
|
+
const contentBlocks = {};
|
|
463
|
+
const usage = {
|
|
464
|
+
inputTokens: 0,
|
|
465
|
+
outputTokens: 0,
|
|
466
|
+
totalTokens: 0
|
|
467
|
+
};
|
|
468
|
+
let finishReason = "unknown";
|
|
471
469
|
try {
|
|
472
|
-
const anthropicStream = client.messages.stream(requestBody
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
let currentToolName = null;
|
|
476
|
-
const usage = {
|
|
477
|
-
inputTokens: 0,
|
|
478
|
-
outputTokens: 0,
|
|
479
|
-
totalTokens: 0
|
|
480
|
-
};
|
|
481
|
-
let finishReason = "unknown";
|
|
470
|
+
const anthropicStream = client.messages.stream(requestBody, {
|
|
471
|
+
signal: options.abortSignal
|
|
472
|
+
});
|
|
482
473
|
controller.enqueue({
|
|
483
474
|
type: "stream-start",
|
|
484
475
|
warnings: []
|
|
485
476
|
});
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
id: event.message.id,
|
|
495
|
-
modelId: event.message.model
|
|
496
|
-
});
|
|
497
|
-
break;
|
|
498
|
-
case "content_block_start":
|
|
499
|
-
if (event.content_block.type === "text") {
|
|
500
|
-
currentTextBlockId = `text-${event.index}`;
|
|
501
|
-
controller.enqueue({
|
|
502
|
-
type: "text-start",
|
|
503
|
-
id: currentTextBlockId
|
|
504
|
-
});
|
|
505
|
-
} else if (event.content_block.type === "tool_use") {
|
|
506
|
-
currentToolBlockId = event.content_block.id;
|
|
507
|
-
currentToolName = event.content_block.name;
|
|
508
|
-
controller.enqueue({
|
|
509
|
-
type: "tool-input-start",
|
|
510
|
-
id: currentToolBlockId,
|
|
511
|
-
toolName: currentToolName
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
break;
|
|
515
|
-
case "content_block_delta":
|
|
516
|
-
if (event.delta.type === "text_delta" && currentTextBlockId) {
|
|
517
|
-
controller.enqueue({
|
|
518
|
-
type: "text-delta",
|
|
519
|
-
id: currentTextBlockId,
|
|
520
|
-
delta: event.delta.text
|
|
521
|
-
});
|
|
522
|
-
} else if (event.delta.type === "input_json_delta" && currentToolBlockId) {
|
|
523
|
-
controller.enqueue({
|
|
524
|
-
type: "tool-input-delta",
|
|
525
|
-
id: currentToolBlockId,
|
|
526
|
-
delta: event.delta.partial_json
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
break;
|
|
530
|
-
case "content_block_stop":
|
|
531
|
-
if (currentTextBlockId) {
|
|
532
|
-
controller.enqueue({
|
|
533
|
-
type: "text-end",
|
|
534
|
-
id: currentTextBlockId
|
|
535
|
-
});
|
|
536
|
-
currentTextBlockId = null;
|
|
537
|
-
}
|
|
538
|
-
if (currentToolBlockId) {
|
|
539
|
-
controller.enqueue({
|
|
540
|
-
type: "tool-input-end",
|
|
541
|
-
id: currentToolBlockId
|
|
542
|
-
});
|
|
543
|
-
currentToolBlockId = null;
|
|
544
|
-
currentToolName = null;
|
|
545
|
-
}
|
|
546
|
-
break;
|
|
547
|
-
case "message_delta":
|
|
548
|
-
if (event.usage) {
|
|
549
|
-
usage.outputTokens = event.usage.output_tokens;
|
|
550
|
-
usage.totalTokens = (usage.inputTokens || 0) + event.usage.output_tokens;
|
|
551
|
-
}
|
|
552
|
-
if (event.delta.stop_reason) {
|
|
553
|
-
finishReason = self.convertFinishReason(event.delta.stop_reason);
|
|
554
|
-
}
|
|
555
|
-
break;
|
|
556
|
-
case "message_stop": {
|
|
557
|
-
const finalMessage = await anthropicStream.finalMessage();
|
|
558
|
-
for (const block of finalMessage.content) {
|
|
559
|
-
if (block.type === "tool_use") {
|
|
477
|
+
await new Promise((resolve2, reject) => {
|
|
478
|
+
anthropicStream.on("streamEvent", (event) => {
|
|
479
|
+
try {
|
|
480
|
+
switch (event.type) {
|
|
481
|
+
case "message_start":
|
|
482
|
+
if (event.message.usage) {
|
|
483
|
+
usage.inputTokens = event.message.usage.input_tokens;
|
|
484
|
+
}
|
|
560
485
|
controller.enqueue({
|
|
561
|
-
type: "
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
input: JSON.stringify(block.input)
|
|
486
|
+
type: "response-metadata",
|
|
487
|
+
id: event.message.id,
|
|
488
|
+
modelId: event.message.model
|
|
565
489
|
});
|
|
490
|
+
break;
|
|
491
|
+
case "content_block_start":
|
|
492
|
+
if (event.content_block.type === "text") {
|
|
493
|
+
const textId = `text-${event.index}`;
|
|
494
|
+
contentBlocks[event.index] = { type: "text", id: textId };
|
|
495
|
+
controller.enqueue({
|
|
496
|
+
type: "text-start",
|
|
497
|
+
id: textId
|
|
498
|
+
});
|
|
499
|
+
} else if (event.content_block.type === "tool_use") {
|
|
500
|
+
contentBlocks[event.index] = {
|
|
501
|
+
type: "tool-call",
|
|
502
|
+
toolCallId: event.content_block.id,
|
|
503
|
+
toolName: event.content_block.name,
|
|
504
|
+
input: ""
|
|
505
|
+
};
|
|
506
|
+
controller.enqueue({
|
|
507
|
+
type: "tool-input-start",
|
|
508
|
+
id: event.content_block.id,
|
|
509
|
+
toolName: event.content_block.name
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
break;
|
|
513
|
+
case "content_block_delta": {
|
|
514
|
+
const block = contentBlocks[event.index];
|
|
515
|
+
if (event.delta.type === "text_delta" && block?.type === "text") {
|
|
516
|
+
controller.enqueue({
|
|
517
|
+
type: "text-delta",
|
|
518
|
+
id: block.id,
|
|
519
|
+
delta: event.delta.text
|
|
520
|
+
});
|
|
521
|
+
} else if (event.delta.type === "input_json_delta" && block?.type === "tool-call") {
|
|
522
|
+
block.input += event.delta.partial_json;
|
|
523
|
+
controller.enqueue({
|
|
524
|
+
type: "tool-input-delta",
|
|
525
|
+
id: block.toolCallId,
|
|
526
|
+
delta: event.delta.partial_json
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case "content_block_stop": {
|
|
532
|
+
const block = contentBlocks[event.index];
|
|
533
|
+
if (block?.type === "text") {
|
|
534
|
+
controller.enqueue({
|
|
535
|
+
type: "text-end",
|
|
536
|
+
id: block.id
|
|
537
|
+
});
|
|
538
|
+
} else if (block?.type === "tool-call") {
|
|
539
|
+
controller.enqueue({
|
|
540
|
+
type: "tool-input-end",
|
|
541
|
+
id: block.toolCallId
|
|
542
|
+
});
|
|
543
|
+
controller.enqueue({
|
|
544
|
+
type: "tool-call",
|
|
545
|
+
toolCallId: block.toolCallId,
|
|
546
|
+
toolName: block.toolName,
|
|
547
|
+
input: block.input === "" ? "{}" : block.input
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
delete contentBlocks[event.index];
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
case "message_delta":
|
|
554
|
+
if (event.usage) {
|
|
555
|
+
usage.outputTokens = event.usage.output_tokens;
|
|
556
|
+
usage.totalTokens = (usage.inputTokens || 0) + event.usage.output_tokens;
|
|
557
|
+
}
|
|
558
|
+
if (event.delta.stop_reason) {
|
|
559
|
+
finishReason = self.convertFinishReason(event.delta.stop_reason);
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
case "message_stop": {
|
|
563
|
+
controller.enqueue({
|
|
564
|
+
type: "finish",
|
|
565
|
+
finishReason,
|
|
566
|
+
usage
|
|
567
|
+
});
|
|
568
|
+
break;
|
|
566
569
|
}
|
|
567
570
|
}
|
|
568
|
-
|
|
569
|
-
type: "finish",
|
|
570
|
-
finishReason,
|
|
571
|
-
usage
|
|
572
|
-
});
|
|
573
|
-
break;
|
|
571
|
+
} catch {
|
|
574
572
|
}
|
|
573
|
+
});
|
|
574
|
+
anthropicStream.on("end", () => {
|
|
575
|
+
resolve2();
|
|
576
|
+
});
|
|
577
|
+
anthropicStream.on("error", (error) => {
|
|
578
|
+
reject(error);
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
for (const [, block] of Object.entries(contentBlocks)) {
|
|
582
|
+
if (block.type === "tool-call") {
|
|
583
|
+
controller.enqueue({
|
|
584
|
+
type: "tool-input-end",
|
|
585
|
+
id: block.toolCallId
|
|
586
|
+
});
|
|
587
|
+
controller.enqueue({
|
|
588
|
+
type: "tool-call",
|
|
589
|
+
toolCallId: block.toolCallId,
|
|
590
|
+
toolName: block.toolName,
|
|
591
|
+
input: block.input === "" ? "{}" : block.input
|
|
592
|
+
});
|
|
575
593
|
}
|
|
576
594
|
}
|
|
577
595
|
controller.close();
|
|
578
596
|
} catch (error) {
|
|
597
|
+
for (const [, block] of Object.entries(contentBlocks)) {
|
|
598
|
+
if (block.type === "tool-call") {
|
|
599
|
+
controller.enqueue({
|
|
600
|
+
type: "tool-input-end",
|
|
601
|
+
id: block.toolCallId
|
|
602
|
+
});
|
|
603
|
+
controller.enqueue({
|
|
604
|
+
type: "tool-call",
|
|
605
|
+
toolCallId: block.toolCallId,
|
|
606
|
+
toolName: block.toolName,
|
|
607
|
+
input: block.input === "" ? "{}" : block.input
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
579
611
|
if (!isRetry && self.isTokenError(error)) {
|
|
580
612
|
self.directAccessClient.invalidateToken();
|
|
581
613
|
controller.enqueue({
|
|
@@ -1045,7 +1077,7 @@ var GitLabProjectCache = class {
|
|
|
1045
1077
|
};
|
|
1046
1078
|
|
|
1047
1079
|
// src/gitlab-project-detector.ts
|
|
1048
|
-
var
|
|
1080
|
+
var debugLog = (..._args) => {
|
|
1049
1081
|
};
|
|
1050
1082
|
var GitLabProjectDetector = class {
|
|
1051
1083
|
config;
|
|
@@ -1074,35 +1106,35 @@ var GitLabProjectDetector = class {
|
|
|
1074
1106
|
return cached;
|
|
1075
1107
|
}
|
|
1076
1108
|
try {
|
|
1077
|
-
|
|
1109
|
+
debugLog(`[GitLabProjectDetector] Getting git remote URL from: ${workingDirectory}`);
|
|
1078
1110
|
const remoteUrl = await this.getGitRemoteUrl(workingDirectory, remoteName);
|
|
1079
1111
|
if (!remoteUrl) {
|
|
1080
|
-
|
|
1112
|
+
debugLog(`[GitLabProjectDetector] No git remote URL found`);
|
|
1081
1113
|
return null;
|
|
1082
1114
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1115
|
+
debugLog(`[GitLabProjectDetector] Git remote URL: ${remoteUrl}`);
|
|
1116
|
+
debugLog(
|
|
1085
1117
|
`[GitLabProjectDetector] Parsing project path from URL (instance: ${this.config.instanceUrl})`
|
|
1086
1118
|
);
|
|
1087
1119
|
const projectPath = this.parseGitRemoteUrl(remoteUrl, this.config.instanceUrl);
|
|
1088
1120
|
if (!projectPath) {
|
|
1089
|
-
|
|
1121
|
+
debugLog(
|
|
1090
1122
|
`[GitLabProjectDetector] Could not parse project path from URL (remote doesn't match instance)`
|
|
1091
1123
|
);
|
|
1092
1124
|
return null;
|
|
1093
1125
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1126
|
+
debugLog(`[GitLabProjectDetector] Parsed project path: ${projectPath}`);
|
|
1127
|
+
debugLog(`[GitLabProjectDetector] Fetching project from GitLab API: ${projectPath}`);
|
|
1096
1128
|
const project = await this.getProjectByPath(projectPath);
|
|
1097
|
-
|
|
1129
|
+
debugLog(`[GitLabProjectDetector] \u2713 Project fetched successfully:`, project);
|
|
1098
1130
|
this.cache.set(cacheKey, project);
|
|
1099
1131
|
return project;
|
|
1100
1132
|
} catch (error) {
|
|
1101
1133
|
if (error instanceof GitLabError) {
|
|
1102
|
-
|
|
1134
|
+
debugLog(`[GitLabProjectDetector] GitLab API error:`, error.message || error);
|
|
1103
1135
|
return null;
|
|
1104
1136
|
}
|
|
1105
|
-
|
|
1137
|
+
debugLog(`[GitLabProjectDetector] Unexpected error:`, error);
|
|
1106
1138
|
console.warn(`Failed to auto-detect GitLab project: ${error}`);
|
|
1107
1139
|
return null;
|
|
1108
1140
|
}
|