gitlab-ai-provider 5.3.3 → 6.1.0
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 +31 -0
- package/dist/gitlab-ai-provider-6.1.0.tgz +0 -0
- package/dist/index.d.mts +92 -48
- package/dist/index.d.ts +92 -48
- package/dist/index.js +319 -117
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +319 -117
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/dist/gitlab-ai-provider-5.3.3.tgz +0 -0
package/dist/index.mjs
CHANGED
|
@@ -171,7 +171,7 @@ var GitLabDirectAccessClient = class {
|
|
|
171
171
|
|
|
172
172
|
// src/gitlab-anthropic-language-model.ts
|
|
173
173
|
var GitLabAnthropicLanguageModel = class {
|
|
174
|
-
specificationVersion = "
|
|
174
|
+
specificationVersion = "v3";
|
|
175
175
|
modelId;
|
|
176
176
|
supportedUrls = {};
|
|
177
177
|
config;
|
|
@@ -330,10 +330,14 @@ ${message.content}` : message.content;
|
|
|
330
330
|
resultContent = part.output.value;
|
|
331
331
|
} else if (part.output.type === "json") {
|
|
332
332
|
resultContent = JSON.stringify(part.output.value);
|
|
333
|
+
} else if (part.output.type === "content") {
|
|
334
|
+
resultContent = JSON.stringify(part.output.value);
|
|
333
335
|
} else if (part.output.type === "error-text") {
|
|
334
336
|
resultContent = part.output.value;
|
|
335
337
|
} else if (part.output.type === "error-json") {
|
|
336
338
|
resultContent = JSON.stringify(part.output.value);
|
|
339
|
+
} else if (part.output.type === "execution-denied") {
|
|
340
|
+
resultContent = part.output.reason ?? "Tool execution denied.";
|
|
337
341
|
} else {
|
|
338
342
|
resultContent = JSON.stringify(part.output);
|
|
339
343
|
}
|
|
@@ -356,18 +360,41 @@ ${message.content}` : message.content;
|
|
|
356
360
|
* Convert Anthropic finish reason to AI SDK format
|
|
357
361
|
*/
|
|
358
362
|
convertFinishReason(stopReason) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
363
|
+
const unified = (() => {
|
|
364
|
+
switch (stopReason) {
|
|
365
|
+
case "end_turn":
|
|
366
|
+
return "stop";
|
|
367
|
+
case "stop_sequence":
|
|
368
|
+
return "stop";
|
|
369
|
+
case "max_tokens":
|
|
370
|
+
return "length";
|
|
371
|
+
case "tool_use":
|
|
372
|
+
return "tool-calls";
|
|
373
|
+
default:
|
|
374
|
+
return "other";
|
|
375
|
+
}
|
|
376
|
+
})();
|
|
377
|
+
return { unified, raw: stopReason ?? void 0 };
|
|
378
|
+
}
|
|
379
|
+
createUsage(params) {
|
|
380
|
+
const inputTotal = params?.inputTotal;
|
|
381
|
+
const outputTotal = params?.outputTotal;
|
|
382
|
+
const cacheRead = params?.cacheRead;
|
|
383
|
+
const cacheWrite = params?.cacheWrite;
|
|
384
|
+
return {
|
|
385
|
+
inputTokens: {
|
|
386
|
+
total: inputTotal,
|
|
387
|
+
noCache: inputTotal == null ? void 0 : Math.max(0, inputTotal - (cacheRead ?? 0) - (cacheWrite ?? 0)),
|
|
388
|
+
cacheRead,
|
|
389
|
+
cacheWrite
|
|
390
|
+
},
|
|
391
|
+
outputTokens: {
|
|
392
|
+
total: outputTotal,
|
|
393
|
+
text: outputTotal,
|
|
394
|
+
reasoning: params?.outputReasoning
|
|
395
|
+
},
|
|
396
|
+
raw: params?.raw
|
|
397
|
+
};
|
|
371
398
|
}
|
|
372
399
|
async doGenerate(options) {
|
|
373
400
|
return this.doGenerateWithRetry(options, false);
|
|
@@ -407,11 +434,13 @@ ${message.content}` : message.content;
|
|
|
407
434
|
});
|
|
408
435
|
}
|
|
409
436
|
}
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
437
|
+
const rawUsage = response.usage;
|
|
438
|
+
const usage = this.createUsage({
|
|
439
|
+
inputTotal: response.usage.input_tokens,
|
|
440
|
+
outputTotal: response.usage.output_tokens,
|
|
441
|
+
cacheRead: rawUsage.cache_read_input_tokens,
|
|
442
|
+
cacheWrite: rawUsage.cache_creation_input_tokens
|
|
443
|
+
});
|
|
415
444
|
return {
|
|
416
445
|
content,
|
|
417
446
|
finishReason: this.convertFinishReason(response.stop_reason),
|
|
@@ -467,12 +496,8 @@ ${message.content}` : message.content;
|
|
|
467
496
|
const stream = new ReadableStream({
|
|
468
497
|
start: async (controller) => {
|
|
469
498
|
const contentBlocks = {};
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
outputTokens: 0,
|
|
473
|
-
totalTokens: 0
|
|
474
|
-
};
|
|
475
|
-
let finishReason = "unknown";
|
|
499
|
+
let usage = self.createUsage();
|
|
500
|
+
let finishReason = { unified: "other", raw: void 0 };
|
|
476
501
|
try {
|
|
477
502
|
const anthropicStream = client.messages.stream(requestBody, {
|
|
478
503
|
signal: options.abortSignal
|
|
@@ -487,7 +512,14 @@ ${message.content}` : message.content;
|
|
|
487
512
|
switch (event.type) {
|
|
488
513
|
case "message_start":
|
|
489
514
|
if (event.message.usage) {
|
|
490
|
-
usage
|
|
515
|
+
usage = self.createUsage({
|
|
516
|
+
inputTotal: event.message.usage.input_tokens,
|
|
517
|
+
outputTotal: usage.outputTokens.total,
|
|
518
|
+
outputReasoning: usage.outputTokens.reasoning,
|
|
519
|
+
cacheRead: usage.inputTokens.cacheRead,
|
|
520
|
+
cacheWrite: usage.inputTokens.cacheWrite,
|
|
521
|
+
raw: usage.raw
|
|
522
|
+
});
|
|
491
523
|
}
|
|
492
524
|
controller.enqueue({
|
|
493
525
|
type: "response-metadata",
|
|
@@ -559,8 +591,14 @@ ${message.content}` : message.content;
|
|
|
559
591
|
}
|
|
560
592
|
case "message_delta":
|
|
561
593
|
if (event.usage) {
|
|
562
|
-
usage
|
|
563
|
-
|
|
594
|
+
usage = self.createUsage({
|
|
595
|
+
inputTotal: usage.inputTokens.total,
|
|
596
|
+
outputTotal: event.usage.output_tokens,
|
|
597
|
+
outputReasoning: usage.outputTokens.reasoning,
|
|
598
|
+
cacheRead: usage.inputTokens.cacheRead,
|
|
599
|
+
cacheWrite: usage.inputTokens.cacheWrite,
|
|
600
|
+
raw: usage.raw
|
|
601
|
+
});
|
|
564
602
|
}
|
|
565
603
|
if (event.delta.stop_reason) {
|
|
566
604
|
finishReason = self.convertFinishReason(event.delta.stop_reason);
|
|
@@ -760,7 +798,7 @@ var MODEL_ID_TO_ANTHROPIC_MODEL = Object.fromEntries(
|
|
|
760
798
|
|
|
761
799
|
// src/gitlab-openai-language-model.ts
|
|
762
800
|
var GitLabOpenAILanguageModel = class {
|
|
763
|
-
specificationVersion = "
|
|
801
|
+
specificationVersion = "v3";
|
|
764
802
|
modelId;
|
|
765
803
|
supportedUrls = {};
|
|
766
804
|
config;
|
|
@@ -906,10 +944,14 @@ var GitLabOpenAILanguageModel = class {
|
|
|
906
944
|
resultContent = part.output.value;
|
|
907
945
|
} else if (part.output.type === "json") {
|
|
908
946
|
resultContent = JSON.stringify(part.output.value);
|
|
947
|
+
} else if (part.output.type === "content") {
|
|
948
|
+
resultContent = JSON.stringify(part.output.value);
|
|
909
949
|
} else if (part.output.type === "error-text") {
|
|
910
950
|
resultContent = part.output.value;
|
|
911
951
|
} else if (part.output.type === "error-json") {
|
|
912
952
|
resultContent = JSON.stringify(part.output.value);
|
|
953
|
+
} else if (part.output.type === "execution-denied") {
|
|
954
|
+
resultContent = part.output.reason ?? "Tool execution denied.";
|
|
913
955
|
} else {
|
|
914
956
|
resultContent = JSON.stringify(part.output);
|
|
915
957
|
}
|
|
@@ -925,18 +967,40 @@ var GitLabOpenAILanguageModel = class {
|
|
|
925
967
|
return messages;
|
|
926
968
|
}
|
|
927
969
|
convertFinishReason(finishReason) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
970
|
+
const unified = (() => {
|
|
971
|
+
switch (finishReason) {
|
|
972
|
+
case "stop":
|
|
973
|
+
return "stop";
|
|
974
|
+
case "length":
|
|
975
|
+
return "length";
|
|
976
|
+
case "tool_calls":
|
|
977
|
+
return "tool-calls";
|
|
978
|
+
case "content_filter":
|
|
979
|
+
return "content-filter";
|
|
980
|
+
default:
|
|
981
|
+
return "other";
|
|
982
|
+
}
|
|
983
|
+
})();
|
|
984
|
+
return { unified, raw: finishReason ?? void 0 };
|
|
985
|
+
}
|
|
986
|
+
createUsage(params) {
|
|
987
|
+
const inputTotal = params?.inputTotal;
|
|
988
|
+
const outputTotal = params?.outputTotal;
|
|
989
|
+
const cacheRead = params?.cacheRead;
|
|
990
|
+
return {
|
|
991
|
+
inputTokens: {
|
|
992
|
+
total: inputTotal,
|
|
993
|
+
noCache: inputTotal == null ? void 0 : Math.max(0, inputTotal - (cacheRead ?? 0)),
|
|
994
|
+
cacheRead,
|
|
995
|
+
cacheWrite: void 0
|
|
996
|
+
},
|
|
997
|
+
outputTokens: {
|
|
998
|
+
total: outputTotal,
|
|
999
|
+
text: outputTotal,
|
|
1000
|
+
reasoning: params?.outputReasoning
|
|
1001
|
+
},
|
|
1002
|
+
raw: params?.raw
|
|
1003
|
+
};
|
|
940
1004
|
}
|
|
941
1005
|
/**
|
|
942
1006
|
* Convert tools to Responses API format
|
|
@@ -953,7 +1017,7 @@ var GitLabOpenAILanguageModel = class {
|
|
|
953
1017
|
name: tool.name,
|
|
954
1018
|
description: tool.description || "",
|
|
955
1019
|
parameters: schema,
|
|
956
|
-
strict:
|
|
1020
|
+
strict: tool.strict
|
|
957
1021
|
};
|
|
958
1022
|
});
|
|
959
1023
|
}
|
|
@@ -1004,10 +1068,14 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1004
1068
|
resultContent = part.output.value;
|
|
1005
1069
|
} else if (part.output.type === "json") {
|
|
1006
1070
|
resultContent = JSON.stringify(part.output.value);
|
|
1071
|
+
} else if (part.output.type === "content") {
|
|
1072
|
+
resultContent = JSON.stringify(part.output.value);
|
|
1007
1073
|
} else if (part.output.type === "error-text") {
|
|
1008
1074
|
resultContent = part.output.value;
|
|
1009
1075
|
} else if (part.output.type === "error-json") {
|
|
1010
1076
|
resultContent = JSON.stringify(part.output.value);
|
|
1077
|
+
} else if (part.output.type === "execution-denied") {
|
|
1078
|
+
resultContent = part.output.reason ?? "Tool execution denied.";
|
|
1011
1079
|
} else {
|
|
1012
1080
|
resultContent = JSON.stringify(part.output);
|
|
1013
1081
|
}
|
|
@@ -1036,20 +1104,23 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1036
1104
|
*/
|
|
1037
1105
|
convertResponsesStatus(status, hasToolCalls = false) {
|
|
1038
1106
|
if (hasToolCalls) {
|
|
1039
|
-
return "tool-calls";
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1107
|
+
return { unified: "tool-calls", raw: status };
|
|
1108
|
+
}
|
|
1109
|
+
const unified = (() => {
|
|
1110
|
+
switch (status) {
|
|
1111
|
+
case "completed":
|
|
1112
|
+
return "stop";
|
|
1113
|
+
case "incomplete":
|
|
1114
|
+
return "length";
|
|
1115
|
+
case "cancelled":
|
|
1116
|
+
return "stop";
|
|
1117
|
+
case "failed":
|
|
1118
|
+
return "error";
|
|
1119
|
+
default:
|
|
1120
|
+
return "other";
|
|
1121
|
+
}
|
|
1122
|
+
})();
|
|
1123
|
+
return { unified, raw: status };
|
|
1053
1124
|
}
|
|
1054
1125
|
async doGenerate(options) {
|
|
1055
1126
|
if (this.useResponsesApi) {
|
|
@@ -1092,11 +1163,12 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1092
1163
|
}
|
|
1093
1164
|
}
|
|
1094
1165
|
}
|
|
1095
|
-
const usage = {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1166
|
+
const usage = this.createUsage({
|
|
1167
|
+
inputTotal: response.usage?.prompt_tokens,
|
|
1168
|
+
outputTotal: response.usage?.completion_tokens,
|
|
1169
|
+
cacheRead: response.usage?.prompt_tokens_details?.cached_tokens,
|
|
1170
|
+
raw: { total_tokens: response.usage?.total_tokens }
|
|
1171
|
+
});
|
|
1100
1172
|
return {
|
|
1101
1173
|
content,
|
|
1102
1174
|
finishReason: this.convertFinishReason(choice?.finish_reason),
|
|
@@ -1163,11 +1235,13 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1163
1235
|
});
|
|
1164
1236
|
}
|
|
1165
1237
|
}
|
|
1166
|
-
const usage = {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1238
|
+
const usage = this.createUsage({
|
|
1239
|
+
inputTotal: response.usage?.input_tokens,
|
|
1240
|
+
outputTotal: response.usage?.output_tokens,
|
|
1241
|
+
outputReasoning: response.usage?.output_tokens_details?.reasoning_tokens,
|
|
1242
|
+
cacheRead: response.usage?.input_tokens_details?.cached_tokens,
|
|
1243
|
+
raw: { total_tokens: response.usage?.total_tokens }
|
|
1244
|
+
});
|
|
1171
1245
|
return {
|
|
1172
1246
|
content,
|
|
1173
1247
|
finishReason: this.convertResponsesStatus(response.status, hasToolCalls),
|
|
@@ -1226,12 +1300,8 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1226
1300
|
const stream = new ReadableStream({
|
|
1227
1301
|
start: async (controller) => {
|
|
1228
1302
|
const toolCalls = {};
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
outputTokens: 0,
|
|
1232
|
-
totalTokens: 0
|
|
1233
|
-
};
|
|
1234
|
-
let finishReason = "unknown";
|
|
1303
|
+
let usage = self.createUsage();
|
|
1304
|
+
let finishReason = { unified: "other", raw: void 0 };
|
|
1235
1305
|
let textStarted = false;
|
|
1236
1306
|
const textId = "text-0";
|
|
1237
1307
|
try {
|
|
@@ -1289,9 +1359,12 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1289
1359
|
finishReason = self.convertFinishReason(choice.finish_reason);
|
|
1290
1360
|
}
|
|
1291
1361
|
if (chunk.usage) {
|
|
1292
|
-
usage
|
|
1293
|
-
|
|
1294
|
-
|
|
1362
|
+
usage = self.createUsage({
|
|
1363
|
+
inputTotal: chunk.usage.prompt_tokens,
|
|
1364
|
+
outputTotal: chunk.usage.completion_tokens,
|
|
1365
|
+
cacheRead: chunk.usage.prompt_tokens_details?.cached_tokens,
|
|
1366
|
+
raw: { total_tokens: chunk.usage.total_tokens }
|
|
1367
|
+
});
|
|
1295
1368
|
}
|
|
1296
1369
|
}
|
|
1297
1370
|
if (textStarted) {
|
|
@@ -1371,12 +1444,8 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1371
1444
|
const stream = new ReadableStream({
|
|
1372
1445
|
start: async (controller) => {
|
|
1373
1446
|
const toolCalls = {};
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
outputTokens: 0,
|
|
1377
|
-
totalTokens: 0
|
|
1378
|
-
};
|
|
1379
|
-
let finishReason = "unknown";
|
|
1447
|
+
let usage = self.createUsage();
|
|
1448
|
+
let finishReason = { unified: "other", raw: void 0 };
|
|
1380
1449
|
let textStarted = false;
|
|
1381
1450
|
const textId = "text-0";
|
|
1382
1451
|
try {
|
|
@@ -1438,9 +1507,13 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1438
1507
|
const hasToolCalls2 = Object.keys(toolCalls).length > 0;
|
|
1439
1508
|
finishReason = self.convertResponsesStatus(event.response.status, hasToolCalls2);
|
|
1440
1509
|
if (event.response.usage) {
|
|
1441
|
-
usage
|
|
1442
|
-
|
|
1443
|
-
|
|
1510
|
+
usage = self.createUsage({
|
|
1511
|
+
inputTotal: event.response.usage.input_tokens,
|
|
1512
|
+
outputTotal: event.response.usage.output_tokens,
|
|
1513
|
+
outputReasoning: event.response.usage.output_tokens_details?.reasoning_tokens,
|
|
1514
|
+
cacheRead: event.response.usage.input_tokens_details?.cached_tokens,
|
|
1515
|
+
raw: { total_tokens: event.response.usage.total_tokens }
|
|
1516
|
+
});
|
|
1444
1517
|
}
|
|
1445
1518
|
}
|
|
1446
1519
|
}
|
|
@@ -1448,8 +1521,8 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1448
1521
|
controller.enqueue({ type: "text-end", id: textId });
|
|
1449
1522
|
}
|
|
1450
1523
|
const hasToolCalls = Object.keys(toolCalls).length > 0;
|
|
1451
|
-
if (hasToolCalls && finishReason === "stop") {
|
|
1452
|
-
finishReason = "tool-calls";
|
|
1524
|
+
if (hasToolCalls && finishReason.unified === "stop") {
|
|
1525
|
+
finishReason = { unified: "tool-calls", raw: finishReason.raw };
|
|
1453
1526
|
}
|
|
1454
1527
|
for (const tc of Object.values(toolCalls)) {
|
|
1455
1528
|
controller.enqueue({ type: "tool-input-end", id: tc.callId });
|
|
@@ -1509,7 +1582,7 @@ var GitLabOpenAILanguageModel = class {
|
|
|
1509
1582
|
import WebSocket from "isomorphic-ws";
|
|
1510
1583
|
|
|
1511
1584
|
// src/version.ts
|
|
1512
|
-
var VERSION = true ? "
|
|
1585
|
+
var VERSION = true ? "6.0.0" : "0.0.0-dev";
|
|
1513
1586
|
|
|
1514
1587
|
// src/gitlab-workflow-types.ts
|
|
1515
1588
|
var WorkflowType = /* @__PURE__ */ ((WorkflowType2) => {
|
|
@@ -1735,7 +1808,9 @@ var GitLabWorkflowClient = class {
|
|
|
1735
1808
|
});
|
|
1736
1809
|
} else if (checkpoint.status === "STOPPED" || checkpoint.status === "CANCELLED") {
|
|
1737
1810
|
this.emit({ type: "completed" });
|
|
1738
|
-
} else if (checkpoint.status === "TOOL_CALL_APPROVAL_REQUIRED"
|
|
1811
|
+
} else if (checkpoint.status === "TOOL_CALL_APPROVAL_REQUIRED") {
|
|
1812
|
+
this.emit({ type: "approval-required", tools: this.extractApprovalTools(checkpoint) });
|
|
1813
|
+
} else if (checkpoint.status === "PLAN_APPROVAL_REQUIRED") {
|
|
1739
1814
|
this.emit({ type: "completed" });
|
|
1740
1815
|
}
|
|
1741
1816
|
return;
|
|
@@ -1774,6 +1849,18 @@ var GitLabWorkflowClient = class {
|
|
|
1774
1849
|
}
|
|
1775
1850
|
}
|
|
1776
1851
|
}
|
|
1852
|
+
extractApprovalTools(checkpoint) {
|
|
1853
|
+
if (!checkpoint.checkpoint) return [];
|
|
1854
|
+
let parsed;
|
|
1855
|
+
try {
|
|
1856
|
+
parsed = JSON.parse(checkpoint.checkpoint);
|
|
1857
|
+
} catch {
|
|
1858
|
+
return [];
|
|
1859
|
+
}
|
|
1860
|
+
return (parsed.channel_values?.ui_chat_log ?? []).filter(
|
|
1861
|
+
(e) => e.message_type === "request" && e.tool_info !== null
|
|
1862
|
+
).map((e) => ({ name: e.tool_info.name, args: JSON.stringify(e.tool_info.args) }));
|
|
1863
|
+
}
|
|
1777
1864
|
send(event) {
|
|
1778
1865
|
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
1779
1866
|
const json = JSON.stringify(event);
|
|
@@ -2873,7 +2960,7 @@ function minimalSchema(schemaStr) {
|
|
|
2873
2960
|
}
|
|
2874
2961
|
}
|
|
2875
2962
|
var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
2876
|
-
specificationVersion = "
|
|
2963
|
+
specificationVersion = "v3";
|
|
2877
2964
|
modelId;
|
|
2878
2965
|
supportedUrls = {};
|
|
2879
2966
|
config;
|
|
@@ -2945,6 +3032,23 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
2945
3032
|
* the AI SDK only surfaces usage via finish-step at stream end.
|
|
2946
3033
|
*/
|
|
2947
3034
|
onUsageUpdate = null;
|
|
3035
|
+
/**
|
|
3036
|
+
* Tool names pre-approved for the current session.
|
|
3037
|
+
* Set by the host (e.g., opencode) and merged into preapproved_tools on each StartRequest.
|
|
3038
|
+
* Updated when the user chooses "always" in the approval prompt.
|
|
3039
|
+
*/
|
|
3040
|
+
sessionPreapprovedTools = [];
|
|
3041
|
+
/**
|
|
3042
|
+
* Set the approval handler callback.
|
|
3043
|
+
* Called when DWS requires tool call approval. Host (e.g., opencode) wires this
|
|
3044
|
+
* to its permission system each stream call, similar to toolExecutor.
|
|
3045
|
+
*/
|
|
3046
|
+
set approvalHandler(handler) {
|
|
3047
|
+
this.workflowOptions.approvalHandler = handler ?? void 0;
|
|
3048
|
+
}
|
|
3049
|
+
get approvalHandler() {
|
|
3050
|
+
return this.workflowOptions.approvalHandler ?? null;
|
|
3051
|
+
}
|
|
2948
3052
|
/**
|
|
2949
3053
|
* Optional callback invoked when multiple workflow models are available
|
|
2950
3054
|
* and the user should pick one. Set per-stream by the host (e.g., OpenCode)
|
|
@@ -3174,16 +3278,34 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3174
3278
|
get workflowId() {
|
|
3175
3279
|
return this.currentWorkflowId;
|
|
3176
3280
|
}
|
|
3281
|
+
createUsage(params) {
|
|
3282
|
+
return {
|
|
3283
|
+
inputTokens: {
|
|
3284
|
+
total: params?.inputTotal,
|
|
3285
|
+
noCache: params?.inputTotal,
|
|
3286
|
+
cacheRead: void 0,
|
|
3287
|
+
cacheWrite: void 0
|
|
3288
|
+
},
|
|
3289
|
+
outputTokens: {
|
|
3290
|
+
total: params?.outputTotal,
|
|
3291
|
+
text: params?.outputTotal,
|
|
3292
|
+
reasoning: void 0
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
createFinishReason(unified, raw) {
|
|
3297
|
+
return { unified, raw };
|
|
3298
|
+
}
|
|
3177
3299
|
// ---------------------------------------------------------------------------
|
|
3178
|
-
//
|
|
3300
|
+
// LanguageModelV3 — doGenerate (non-streaming)
|
|
3179
3301
|
// ---------------------------------------------------------------------------
|
|
3180
3302
|
async doGenerate(options) {
|
|
3181
3303
|
const { stream } = await this.doStream(options);
|
|
3182
3304
|
const reader = stream.getReader();
|
|
3183
3305
|
const textParts = [];
|
|
3184
3306
|
const toolCalls = [];
|
|
3185
|
-
let finishReason = "
|
|
3186
|
-
|
|
3307
|
+
let finishReason = { unified: "other", raw: void 0 };
|
|
3308
|
+
let usage = this.createUsage();
|
|
3187
3309
|
try {
|
|
3188
3310
|
while (true) {
|
|
3189
3311
|
const { done, value } = await reader.read();
|
|
@@ -3203,9 +3325,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3203
3325
|
case "finish":
|
|
3204
3326
|
finishReason = value.finishReason;
|
|
3205
3327
|
if (value.usage) {
|
|
3206
|
-
usage
|
|
3207
|
-
usage.outputTokens = value.usage.outputTokens ?? 0;
|
|
3208
|
-
usage.totalTokens = value.usage.totalTokens ?? 0;
|
|
3328
|
+
usage = value.usage;
|
|
3209
3329
|
}
|
|
3210
3330
|
break;
|
|
3211
3331
|
case "error":
|
|
@@ -3224,13 +3344,16 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3224
3344
|
return { content, finishReason, usage, warnings: [] };
|
|
3225
3345
|
}
|
|
3226
3346
|
// ---------------------------------------------------------------------------
|
|
3227
|
-
//
|
|
3347
|
+
// LanguageModelV3 — doStream (streaming)
|
|
3228
3348
|
// ---------------------------------------------------------------------------
|
|
3229
3349
|
async doStream(options) {
|
|
3230
3350
|
const goal = this.extractGoalFromPrompt(options.prompt);
|
|
3231
3351
|
const modelRef = await this.resolveModelRef();
|
|
3232
3352
|
const mcpTools = this.extractMcpTools(options);
|
|
3233
|
-
const preapprovedTools =
|
|
3353
|
+
const preapprovedTools = [
|
|
3354
|
+
...this.workflowOptions.preapprovedTools ?? mcpTools.map((t) => t.name),
|
|
3355
|
+
...this.sessionPreapprovedTools
|
|
3356
|
+
];
|
|
3234
3357
|
const additionalContext = this.buildAdditionalContext(options.prompt);
|
|
3235
3358
|
const toolExecutor = this.toolExecutor ?? null;
|
|
3236
3359
|
const availableToolNames = new Set(options.tools?.map((t) => t.name) ?? []);
|
|
@@ -3246,7 +3369,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3246
3369
|
workflowId = await this.tokenClient.createWorkflow(goal, {
|
|
3247
3370
|
projectId,
|
|
3248
3371
|
namespaceId: this.workflowOptions.namespaceId,
|
|
3249
|
-
workflowDefinition: this.workflowOptions.workflowDefinition
|
|
3372
|
+
workflowDefinition: this.workflowOptions.workflowDefinition,
|
|
3373
|
+
agentPrivileges: this.workflowOptions.agentPrivileges
|
|
3250
3374
|
});
|
|
3251
3375
|
this.currentWorkflowId = workflowId;
|
|
3252
3376
|
}
|
|
@@ -3258,11 +3382,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3258
3382
|
streamedInputChars: 0,
|
|
3259
3383
|
streamedOutputChars: 0,
|
|
3260
3384
|
pendingToolCount: 0,
|
|
3385
|
+
approvalPending: false,
|
|
3261
3386
|
deferredClose: null,
|
|
3262
3387
|
activeTextBlockId: null,
|
|
3263
3388
|
agentMessageEmitted: new Map(this.persistedAgentEmitted),
|
|
3264
3389
|
currentAgentMessageId: "",
|
|
3265
|
-
activeClient: wsClient
|
|
3390
|
+
activeClient: wsClient,
|
|
3391
|
+
processedRequestIDs: /* @__PURE__ */ new Set()
|
|
3266
3392
|
};
|
|
3267
3393
|
for (const msg of options.prompt) {
|
|
3268
3394
|
if (msg.role === "system") {
|
|
@@ -3275,6 +3401,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3275
3401
|
}
|
|
3276
3402
|
}
|
|
3277
3403
|
}
|
|
3404
|
+
let startReq;
|
|
3278
3405
|
const stream = new ReadableStream({
|
|
3279
3406
|
start: async (controller) => {
|
|
3280
3407
|
try {
|
|
@@ -3295,7 +3422,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3295
3422
|
wsClient,
|
|
3296
3423
|
toolExecutor,
|
|
3297
3424
|
() => `text-${textBlockCounter++}`,
|
|
3298
|
-
availableToolNames
|
|
3425
|
+
availableToolNames,
|
|
3426
|
+
startReq
|
|
3299
3427
|
);
|
|
3300
3428
|
}
|
|
3301
3429
|
);
|
|
@@ -3317,7 +3445,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3317
3445
|
const trimmedPreapproved = preapprovedTools.filter(
|
|
3318
3446
|
(name) => trimmed.mcpTools.some((t) => t.name === name)
|
|
3319
3447
|
);
|
|
3320
|
-
|
|
3448
|
+
startReq = {
|
|
3321
3449
|
workflowID: workflowId,
|
|
3322
3450
|
clientVersion: CLIENT_VERSION,
|
|
3323
3451
|
workflowDefinition: workflowDef,
|
|
@@ -3382,7 +3510,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3382
3510
|
// ---------------------------------------------------------------------------
|
|
3383
3511
|
// Event handling
|
|
3384
3512
|
// ---------------------------------------------------------------------------
|
|
3385
|
-
handleWorkflowEvent(ss, event, controller, wsClient, toolExecutor, nextTextId, availableToolNames) {
|
|
3513
|
+
handleWorkflowEvent(ss, event, controller, wsClient, toolExecutor, nextTextId, availableToolNames, startReq) {
|
|
3386
3514
|
if (ss.streamClosed) {
|
|
3387
3515
|
return;
|
|
3388
3516
|
}
|
|
@@ -3393,6 +3521,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3393
3521
|
}
|
|
3394
3522
|
case "tool-request": {
|
|
3395
3523
|
const { requestID, data } = event;
|
|
3524
|
+
if (ss.processedRequestIDs.has(requestID)) break;
|
|
3525
|
+
ss.processedRequestIDs.add(requestID);
|
|
3396
3526
|
let parsedArgs;
|
|
3397
3527
|
try {
|
|
3398
3528
|
JSON.parse(data.args);
|
|
@@ -3439,6 +3569,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3439
3569
|
break;
|
|
3440
3570
|
}
|
|
3441
3571
|
case "builtin-tool-request": {
|
|
3572
|
+
if (ss.processedRequestIDs.has(event.requestID)) break;
|
|
3573
|
+
ss.processedRequestIDs.add(event.requestID);
|
|
3442
3574
|
const mapped = mapBuiltinTool(event.toolName, event.data, availableToolNames);
|
|
3443
3575
|
const mappedArgs = JSON.stringify(mapped.args);
|
|
3444
3576
|
if (ss.activeTextBlockId) {
|
|
@@ -3479,6 +3611,26 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3479
3611
|
});
|
|
3480
3612
|
break;
|
|
3481
3613
|
}
|
|
3614
|
+
case "approval-required": {
|
|
3615
|
+
ss.approvalPending = true;
|
|
3616
|
+
this.approveAndResume(
|
|
3617
|
+
ss,
|
|
3618
|
+
event.tools,
|
|
3619
|
+
startReq,
|
|
3620
|
+
controller,
|
|
3621
|
+
toolExecutor,
|
|
3622
|
+
nextTextId,
|
|
3623
|
+
availableToolNames
|
|
3624
|
+
).catch(() => {
|
|
3625
|
+
ss.approvalPending = false;
|
|
3626
|
+
if (ss.deferredClose) {
|
|
3627
|
+
const close = ss.deferredClose;
|
|
3628
|
+
ss.deferredClose = null;
|
|
3629
|
+
close();
|
|
3630
|
+
}
|
|
3631
|
+
});
|
|
3632
|
+
break;
|
|
3633
|
+
}
|
|
3482
3634
|
case "completed": {
|
|
3483
3635
|
if (ss.activeTextBlockId) {
|
|
3484
3636
|
controller.enqueue({ type: "text-end", id: ss.activeTextBlockId });
|
|
@@ -3490,14 +3642,14 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3490
3642
|
const outputTokens = Math.ceil(ss.streamedOutputChars / 4);
|
|
3491
3643
|
controller.enqueue({
|
|
3492
3644
|
type: "finish",
|
|
3493
|
-
finishReason: "stop",
|
|
3494
|
-
usage: { inputTokens,
|
|
3645
|
+
finishReason: this.createFinishReason("stop", "completed"),
|
|
3646
|
+
usage: this.createUsage({ inputTotal: inputTokens, outputTotal: outputTokens })
|
|
3495
3647
|
});
|
|
3496
3648
|
ss.streamClosed = true;
|
|
3497
3649
|
controller.close();
|
|
3498
3650
|
this.cleanupClient(ss);
|
|
3499
3651
|
};
|
|
3500
|
-
if (ss.pendingToolCount > 0) {
|
|
3652
|
+
if (ss.pendingToolCount > 0 || ss.approvalPending) {
|
|
3501
3653
|
ss.deferredClose = doCompleteClose;
|
|
3502
3654
|
} else {
|
|
3503
3655
|
ss.deferredClose = null;
|
|
@@ -3548,15 +3700,15 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3548
3700
|
const outTok = Math.ceil(ss.streamedOutputChars / 4);
|
|
3549
3701
|
controller.enqueue({
|
|
3550
3702
|
type: "finish",
|
|
3551
|
-
finishReason: "stop",
|
|
3552
|
-
usage: {
|
|
3703
|
+
finishReason: this.createFinishReason("stop", "closed"),
|
|
3704
|
+
usage: this.createUsage({ inputTotal: inTok, outputTotal: outTok })
|
|
3553
3705
|
});
|
|
3554
3706
|
ss.streamClosed = true;
|
|
3555
3707
|
controller.close();
|
|
3556
3708
|
this.cleanupClient(ss);
|
|
3557
3709
|
}
|
|
3558
3710
|
};
|
|
3559
|
-
if (ss.pendingToolCount > 0) {
|
|
3711
|
+
if (ss.pendingToolCount > 0 || ss.approvalPending) {
|
|
3560
3712
|
ss.deferredClose = doClose;
|
|
3561
3713
|
} else {
|
|
3562
3714
|
ss.deferredClose = null;
|
|
@@ -3666,8 +3818,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3666
3818
|
toolCallId: requestID,
|
|
3667
3819
|
toolName,
|
|
3668
3820
|
result: errorText,
|
|
3669
|
-
isError: true
|
|
3670
|
-
providerExecuted: true
|
|
3821
|
+
isError: true
|
|
3671
3822
|
});
|
|
3672
3823
|
} else {
|
|
3673
3824
|
safeEnqueue({
|
|
@@ -3679,8 +3830,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3679
3830
|
title: toolTitle,
|
|
3680
3831
|
metadata: toolMetadata
|
|
3681
3832
|
},
|
|
3682
|
-
isError: false
|
|
3683
|
-
providerExecuted: true
|
|
3833
|
+
isError: false
|
|
3684
3834
|
});
|
|
3685
3835
|
}
|
|
3686
3836
|
} else {
|
|
@@ -3691,8 +3841,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3691
3841
|
toolCallId: requestID,
|
|
3692
3842
|
toolName,
|
|
3693
3843
|
result: errorMsg,
|
|
3694
|
-
isError: true
|
|
3695
|
-
providerExecuted: true
|
|
3844
|
+
isError: true
|
|
3696
3845
|
});
|
|
3697
3846
|
}
|
|
3698
3847
|
} catch (error) {
|
|
@@ -3704,8 +3853,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3704
3853
|
toolCallId: requestID,
|
|
3705
3854
|
toolName,
|
|
3706
3855
|
result: errorMsg,
|
|
3707
|
-
isError: true
|
|
3708
|
-
providerExecuted: true
|
|
3856
|
+
isError: true
|
|
3709
3857
|
});
|
|
3710
3858
|
} finally {
|
|
3711
3859
|
ss.pendingToolCount--;
|
|
@@ -3736,6 +3884,60 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
|
|
|
3736
3884
|
this.persistedAgentEmitted.clear();
|
|
3737
3885
|
}
|
|
3738
3886
|
}
|
|
3887
|
+
async approveAndResume(ss, tools, startReq, controller, toolExecutor, nextTextId, availableToolNames) {
|
|
3888
|
+
const handler = this.workflowOptions.approvalHandler;
|
|
3889
|
+
if (!handler || !startReq) {
|
|
3890
|
+
ss.approvalPending = false;
|
|
3891
|
+
if (ss.deferredClose) {
|
|
3892
|
+
const close = ss.deferredClose;
|
|
3893
|
+
ss.deferredClose = null;
|
|
3894
|
+
close();
|
|
3895
|
+
}
|
|
3896
|
+
return;
|
|
3897
|
+
}
|
|
3898
|
+
let decision;
|
|
3899
|
+
try {
|
|
3900
|
+
decision = await handler(tools);
|
|
3901
|
+
} catch (err) {
|
|
3902
|
+
ss.approvalPending = false;
|
|
3903
|
+
if (!ss.streamClosed) controller.error(err);
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3906
|
+
ss.approvalPending = false;
|
|
3907
|
+
this.cleanupClient(ss, false);
|
|
3908
|
+
const approval = decision.approved ? { approval: { tool_name: tools[0]?.name, tool_args_json: tools[0]?.args } } : { rejection: { message: decision.message ?? "User rejected" } };
|
|
3909
|
+
const newStartReq = { ...startReq, approval };
|
|
3910
|
+
const newClient = new GitLabWorkflowClient();
|
|
3911
|
+
this.activeClients.add(newClient);
|
|
3912
|
+
ss.activeClient = newClient;
|
|
3913
|
+
const modelRef = await this.resolveModelRef();
|
|
3914
|
+
try {
|
|
3915
|
+
await newClient.connect(
|
|
3916
|
+
{
|
|
3917
|
+
instanceUrl: this.config.instanceUrl,
|
|
3918
|
+
modelRef,
|
|
3919
|
+
headers: this.config.getHeaders(),
|
|
3920
|
+
projectId: this.workflowOptions.projectId,
|
|
3921
|
+
namespaceId: this.workflowOptions.namespaceId,
|
|
3922
|
+
rootNamespaceId: this.workflowOptions.rootNamespaceId
|
|
3923
|
+
},
|
|
3924
|
+
(event) => this.handleWorkflowEvent(
|
|
3925
|
+
ss,
|
|
3926
|
+
event,
|
|
3927
|
+
controller,
|
|
3928
|
+
newClient,
|
|
3929
|
+
toolExecutor,
|
|
3930
|
+
nextTextId,
|
|
3931
|
+
availableToolNames,
|
|
3932
|
+
newStartReq
|
|
3933
|
+
)
|
|
3934
|
+
);
|
|
3935
|
+
newClient.sendStartRequest(newStartReq);
|
|
3936
|
+
} catch (err) {
|
|
3937
|
+
this.cleanupClient(ss, true);
|
|
3938
|
+
if (!ss.streamClosed) controller.error(err);
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3739
3941
|
// ---------------------------------------------------------------------------
|
|
3740
3942
|
// Workflow metadata
|
|
3741
3943
|
// ---------------------------------------------------------------------------
|
|
@@ -4304,13 +4506,13 @@ function createGitLab(options = {}) {
|
|
|
4304
4506
|
return createAgenticChatModel(modelId);
|
|
4305
4507
|
};
|
|
4306
4508
|
const provider = Object.assign((modelId) => createDefaultModel(modelId), {
|
|
4307
|
-
specificationVersion: "
|
|
4509
|
+
specificationVersion: "v3",
|
|
4308
4510
|
languageModel: createDefaultModel,
|
|
4309
4511
|
chat: createDefaultModel,
|
|
4310
4512
|
agenticChat: createAgenticChatModel,
|
|
4311
4513
|
workflowChat: createWorkflowChatModel
|
|
4312
4514
|
});
|
|
4313
|
-
provider.
|
|
4515
|
+
provider.embeddingModel = (modelId) => {
|
|
4314
4516
|
throw new GitLabError({
|
|
4315
4517
|
message: `GitLab provider does not support text embedding models. Model ID: ${modelId}`
|
|
4316
4518
|
});
|