@gitlab/gitlab-ai-provider 3.1.3 → 3.2.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 +4 -0
- package/README.md +117 -29
- package/dist/gitlab-gitlab-ai-provider-3.2.0.tgz +0 -0
- package/dist/index.d.mts +113 -46
- package/dist/index.d.ts +113 -46
- package/dist/index.js +791 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +781 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/dist/gitlab-gitlab-ai-provider-3.1.3.tgz +0 -0
package/dist/index.js
CHANGED
|
@@ -32,22 +32,30 @@ __export(index_exports, {
|
|
|
32
32
|
BUNDLED_CLIENT_ID: () => BUNDLED_CLIENT_ID,
|
|
33
33
|
DEFAULT_AI_GATEWAY_URL: () => DEFAULT_AI_GATEWAY_URL,
|
|
34
34
|
GITLAB_COM_URL: () => GITLAB_COM_URL,
|
|
35
|
-
|
|
35
|
+
GitLabAnthropicLanguageModel: () => GitLabAnthropicLanguageModel,
|
|
36
36
|
GitLabDirectAccessClient: () => GitLabDirectAccessClient,
|
|
37
37
|
GitLabError: () => GitLabError,
|
|
38
38
|
GitLabOAuthManager: () => GitLabOAuthManager,
|
|
39
|
+
GitLabOpenAILanguageModel: () => GitLabOpenAILanguageModel,
|
|
39
40
|
GitLabProjectCache: () => GitLabProjectCache,
|
|
40
41
|
GitLabProjectDetector: () => GitLabProjectDetector,
|
|
41
42
|
MODEL_ID_TO_ANTHROPIC_MODEL: () => MODEL_ID_TO_ANTHROPIC_MODEL,
|
|
43
|
+
MODEL_MAPPINGS: () => MODEL_MAPPINGS,
|
|
42
44
|
OAUTH_SCOPES: () => OAUTH_SCOPES,
|
|
43
45
|
TOKEN_EXPIRY_SKEW_MS: () => TOKEN_EXPIRY_SKEW_MS,
|
|
44
46
|
createGitLab: () => createGitLab,
|
|
45
47
|
getAnthropicModelForModelId: () => getAnthropicModelForModelId,
|
|
46
|
-
|
|
48
|
+
getModelMapping: () => getModelMapping,
|
|
49
|
+
getOpenAIApiType: () => getOpenAIApiType,
|
|
50
|
+
getOpenAIModelForModelId: () => getOpenAIModelForModelId,
|
|
51
|
+
getProviderForModelId: () => getProviderForModelId,
|
|
52
|
+
getValidModelsForProvider: () => getValidModelsForProvider,
|
|
53
|
+
gitlab: () => gitlab,
|
|
54
|
+
isResponsesApiModel: () => isResponsesApiModel
|
|
47
55
|
});
|
|
48
56
|
module.exports = __toCommonJS(index_exports);
|
|
49
57
|
|
|
50
|
-
// src/gitlab-
|
|
58
|
+
// src/gitlab-anthropic-language-model.ts
|
|
51
59
|
var import_sdk = __toESM(require("@anthropic-ai/sdk"));
|
|
52
60
|
|
|
53
61
|
// src/gitlab-direct-access.ts
|
|
@@ -182,6 +190,15 @@ var GitLabDirectAccessClient = class {
|
|
|
182
190
|
const baseUrl = this.aiGatewayUrl.replace(/\/$/, "");
|
|
183
191
|
return `${baseUrl}/ai/v1/proxy/anthropic/`;
|
|
184
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Get the OpenAI proxy base URL
|
|
195
|
+
* Note: The OpenAI SDK expects a base URL like https://api.openai.com/v1
|
|
196
|
+
* and appends paths like /chat/completions. So we need /v1 at the end.
|
|
197
|
+
*/
|
|
198
|
+
getOpenAIProxyUrl() {
|
|
199
|
+
const baseUrl = this.aiGatewayUrl.replace(/\/$/, "");
|
|
200
|
+
return `${baseUrl}/ai/v1/proxy/openai/v1`;
|
|
201
|
+
}
|
|
185
202
|
/**
|
|
186
203
|
* Invalidate the cached token
|
|
187
204
|
*/
|
|
@@ -191,8 +208,8 @@ var GitLabDirectAccessClient = class {
|
|
|
191
208
|
}
|
|
192
209
|
};
|
|
193
210
|
|
|
194
|
-
// src/gitlab-
|
|
195
|
-
var
|
|
211
|
+
// src/gitlab-anthropic-language-model.ts
|
|
212
|
+
var GitLabAnthropicLanguageModel = class {
|
|
196
213
|
specificationVersion = "v2";
|
|
197
214
|
modelId;
|
|
198
215
|
supportedUrls = {};
|
|
@@ -645,6 +662,740 @@ var GitLabAgenticLanguageModel = class {
|
|
|
645
662
|
}
|
|
646
663
|
};
|
|
647
664
|
|
|
665
|
+
// src/gitlab-openai-language-model.ts
|
|
666
|
+
var import_openai = __toESM(require("openai"));
|
|
667
|
+
|
|
668
|
+
// src/model-mappings.ts
|
|
669
|
+
var MODEL_MAPPINGS = {
|
|
670
|
+
// Anthropic models
|
|
671
|
+
"duo-chat-opus-4-5": { provider: "anthropic", model: "claude-opus-4-5-20251101" },
|
|
672
|
+
"duo-chat-sonnet-4-5": { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
|
|
673
|
+
"duo-chat-haiku-4-5": { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
|
|
674
|
+
// OpenAI models - Chat Completions API
|
|
675
|
+
"duo-chat-gpt-5-1": { provider: "openai", model: "gpt-5.1-2025-11-13", openaiApiType: "chat" },
|
|
676
|
+
"duo-chat-gpt-5-mini": {
|
|
677
|
+
provider: "openai",
|
|
678
|
+
model: "gpt-5-mini-2025-08-07",
|
|
679
|
+
openaiApiType: "chat"
|
|
680
|
+
},
|
|
681
|
+
// OpenAI models - Responses API (Codex models)
|
|
682
|
+
"duo-chat-gpt-5-codex": { provider: "openai", model: "gpt-5-codex", openaiApiType: "responses" },
|
|
683
|
+
"duo-chat-gpt-5-2-codex": {
|
|
684
|
+
provider: "openai",
|
|
685
|
+
model: "gpt-5.2-codex",
|
|
686
|
+
openaiApiType: "responses"
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
function getModelMapping(modelId) {
|
|
690
|
+
return MODEL_MAPPINGS[modelId];
|
|
691
|
+
}
|
|
692
|
+
function getProviderForModelId(modelId) {
|
|
693
|
+
return MODEL_MAPPINGS[modelId]?.provider;
|
|
694
|
+
}
|
|
695
|
+
function getValidModelsForProvider(provider) {
|
|
696
|
+
return Object.values(MODEL_MAPPINGS).filter((m) => m.provider === provider).map((m) => m.model);
|
|
697
|
+
}
|
|
698
|
+
function getAnthropicModelForModelId(modelId) {
|
|
699
|
+
const mapping = MODEL_MAPPINGS[modelId];
|
|
700
|
+
return mapping?.provider === "anthropic" ? mapping.model : void 0;
|
|
701
|
+
}
|
|
702
|
+
function getOpenAIModelForModelId(modelId) {
|
|
703
|
+
const mapping = MODEL_MAPPINGS[modelId];
|
|
704
|
+
return mapping?.provider === "openai" ? mapping.model : void 0;
|
|
705
|
+
}
|
|
706
|
+
function getOpenAIApiType(modelId) {
|
|
707
|
+
const mapping = MODEL_MAPPINGS[modelId];
|
|
708
|
+
return mapping?.openaiApiType ?? "chat";
|
|
709
|
+
}
|
|
710
|
+
function isResponsesApiModel(modelId) {
|
|
711
|
+
return getOpenAIApiType(modelId) === "responses";
|
|
712
|
+
}
|
|
713
|
+
var MODEL_ID_TO_ANTHROPIC_MODEL = Object.fromEntries(
|
|
714
|
+
Object.entries(MODEL_MAPPINGS).filter(([, v]) => v.provider === "anthropic").map(([k, v]) => [k, v.model])
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
// src/gitlab-openai-language-model.ts
|
|
718
|
+
var GitLabOpenAILanguageModel = class {
|
|
719
|
+
specificationVersion = "v2";
|
|
720
|
+
modelId;
|
|
721
|
+
supportedUrls = {};
|
|
722
|
+
config;
|
|
723
|
+
directAccessClient;
|
|
724
|
+
useResponsesApi;
|
|
725
|
+
openaiClient = null;
|
|
726
|
+
constructor(modelId, config) {
|
|
727
|
+
this.modelId = modelId;
|
|
728
|
+
this.config = config;
|
|
729
|
+
this.useResponsesApi = config.useResponsesApi ?? isResponsesApiModel(modelId);
|
|
730
|
+
this.directAccessClient = new GitLabDirectAccessClient({
|
|
731
|
+
instanceUrl: config.instanceUrl,
|
|
732
|
+
getHeaders: config.getHeaders,
|
|
733
|
+
refreshApiKey: config.refreshApiKey,
|
|
734
|
+
fetch: config.fetch,
|
|
735
|
+
featureFlags: config.featureFlags,
|
|
736
|
+
aiGatewayUrl: config.aiGatewayUrl
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
get provider() {
|
|
740
|
+
return this.config.provider;
|
|
741
|
+
}
|
|
742
|
+
async getOpenAIClient(forceRefresh = false) {
|
|
743
|
+
const tokenData = await this.directAccessClient.getDirectAccessToken(forceRefresh);
|
|
744
|
+
const { "x-api-key": _removed, ...filteredHeaders } = tokenData.headers;
|
|
745
|
+
this.openaiClient = new import_openai.default({
|
|
746
|
+
apiKey: tokenData.token,
|
|
747
|
+
baseURL: this.directAccessClient.getOpenAIProxyUrl(),
|
|
748
|
+
defaultHeaders: filteredHeaders
|
|
749
|
+
});
|
|
750
|
+
return this.openaiClient;
|
|
751
|
+
}
|
|
752
|
+
isTokenError(error) {
|
|
753
|
+
if (error instanceof import_openai.default.APIError) {
|
|
754
|
+
if (error.status === 401) {
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
const message = error.message?.toLowerCase() || "";
|
|
758
|
+
if (message.includes("token") && (message.includes("expired") || message.includes("revoked") || message.includes("invalid"))) {
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
convertTools(tools) {
|
|
765
|
+
if (!tools || tools.length === 0) {
|
|
766
|
+
return void 0;
|
|
767
|
+
}
|
|
768
|
+
return tools.filter((tool) => tool.type === "function").map((tool) => {
|
|
769
|
+
const schema = tool.inputSchema;
|
|
770
|
+
return {
|
|
771
|
+
type: "function",
|
|
772
|
+
function: {
|
|
773
|
+
name: tool.name,
|
|
774
|
+
description: tool.description || "",
|
|
775
|
+
// Ensure the schema has type: 'object' as OpenAI requires it
|
|
776
|
+
parameters: {
|
|
777
|
+
type: "object",
|
|
778
|
+
...schema
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
convertToolChoice(toolChoice) {
|
|
785
|
+
if (!toolChoice) {
|
|
786
|
+
return void 0;
|
|
787
|
+
}
|
|
788
|
+
switch (toolChoice.type) {
|
|
789
|
+
case "auto":
|
|
790
|
+
return "auto";
|
|
791
|
+
case "none":
|
|
792
|
+
return "none";
|
|
793
|
+
case "required":
|
|
794
|
+
return "required";
|
|
795
|
+
case "tool":
|
|
796
|
+
return { type: "function", function: { name: toolChoice.toolName } };
|
|
797
|
+
default:
|
|
798
|
+
return void 0;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
convertPrompt(prompt) {
|
|
802
|
+
const messages = [];
|
|
803
|
+
for (const message of prompt) {
|
|
804
|
+
if (message.role === "system") {
|
|
805
|
+
messages.push({ role: "system", content: message.content });
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (message.role === "user") {
|
|
809
|
+
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text);
|
|
810
|
+
if (textParts.length > 0) {
|
|
811
|
+
messages.push({ role: "user", content: textParts.join("\n") });
|
|
812
|
+
}
|
|
813
|
+
} else if (message.role === "assistant") {
|
|
814
|
+
const textParts = [];
|
|
815
|
+
const toolCalls = [];
|
|
816
|
+
for (const part of message.content) {
|
|
817
|
+
if (part.type === "text") {
|
|
818
|
+
textParts.push(part.text);
|
|
819
|
+
} else if (part.type === "tool-call") {
|
|
820
|
+
toolCalls.push({
|
|
821
|
+
id: part.toolCallId,
|
|
822
|
+
type: "function",
|
|
823
|
+
function: {
|
|
824
|
+
name: part.toolName,
|
|
825
|
+
arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input)
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
const assistantMessage = {
|
|
831
|
+
role: "assistant",
|
|
832
|
+
content: textParts.length > 0 ? textParts.join("\n") : null
|
|
833
|
+
};
|
|
834
|
+
if (toolCalls.length > 0) {
|
|
835
|
+
assistantMessage.tool_calls = toolCalls;
|
|
836
|
+
}
|
|
837
|
+
messages.push(assistantMessage);
|
|
838
|
+
} else if (message.role === "tool") {
|
|
839
|
+
for (const part of message.content) {
|
|
840
|
+
if (part.type === "tool-result") {
|
|
841
|
+
let resultContent;
|
|
842
|
+
if (part.output.type === "text") {
|
|
843
|
+
resultContent = part.output.value;
|
|
844
|
+
} else if (part.output.type === "json") {
|
|
845
|
+
resultContent = JSON.stringify(part.output.value);
|
|
846
|
+
} else if (part.output.type === "error-text") {
|
|
847
|
+
resultContent = part.output.value;
|
|
848
|
+
} else if (part.output.type === "error-json") {
|
|
849
|
+
resultContent = JSON.stringify(part.output.value);
|
|
850
|
+
} else {
|
|
851
|
+
resultContent = JSON.stringify(part.output);
|
|
852
|
+
}
|
|
853
|
+
messages.push({
|
|
854
|
+
role: "tool",
|
|
855
|
+
tool_call_id: part.toolCallId,
|
|
856
|
+
content: resultContent
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return messages;
|
|
863
|
+
}
|
|
864
|
+
convertFinishReason(finishReason) {
|
|
865
|
+
switch (finishReason) {
|
|
866
|
+
case "stop":
|
|
867
|
+
return "stop";
|
|
868
|
+
case "length":
|
|
869
|
+
return "length";
|
|
870
|
+
case "tool_calls":
|
|
871
|
+
return "tool-calls";
|
|
872
|
+
case "content_filter":
|
|
873
|
+
return "content-filter";
|
|
874
|
+
default:
|
|
875
|
+
return "unknown";
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Convert tools to Responses API format
|
|
880
|
+
*/
|
|
881
|
+
convertToolsForResponses(tools) {
|
|
882
|
+
if (!tools || tools.length === 0) {
|
|
883
|
+
return void 0;
|
|
884
|
+
}
|
|
885
|
+
return tools.filter((tool) => tool.type === "function").map((tool) => {
|
|
886
|
+
const schema = { ...tool.inputSchema };
|
|
887
|
+
delete schema["$schema"];
|
|
888
|
+
return {
|
|
889
|
+
type: "function",
|
|
890
|
+
name: tool.name,
|
|
891
|
+
description: tool.description || "",
|
|
892
|
+
parameters: schema,
|
|
893
|
+
strict: false
|
|
894
|
+
};
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Convert prompt to Responses API input format
|
|
899
|
+
*/
|
|
900
|
+
convertPromptForResponses(prompt) {
|
|
901
|
+
const items = [];
|
|
902
|
+
for (const message of prompt) {
|
|
903
|
+
if (message.role === "system") {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (message.role === "user") {
|
|
907
|
+
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text);
|
|
908
|
+
if (textParts.length > 0) {
|
|
909
|
+
items.push({
|
|
910
|
+
type: "message",
|
|
911
|
+
role: "user",
|
|
912
|
+
content: textParts.map((text) => ({ type: "input_text", text }))
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
} else if (message.role === "assistant") {
|
|
916
|
+
const textParts = [];
|
|
917
|
+
for (const part of message.content) {
|
|
918
|
+
if (part.type === "text") {
|
|
919
|
+
textParts.push(part.text);
|
|
920
|
+
} else if (part.type === "tool-call") {
|
|
921
|
+
items.push({
|
|
922
|
+
type: "function_call",
|
|
923
|
+
call_id: part.toolCallId,
|
|
924
|
+
name: part.toolName,
|
|
925
|
+
arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input)
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (textParts.length > 0) {
|
|
930
|
+
items.push({
|
|
931
|
+
type: "message",
|
|
932
|
+
role: "assistant",
|
|
933
|
+
content: [{ type: "output_text", text: textParts.join("\n"), annotations: [] }]
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
} else if (message.role === "tool") {
|
|
937
|
+
for (const part of message.content) {
|
|
938
|
+
if (part.type === "tool-result") {
|
|
939
|
+
let resultContent;
|
|
940
|
+
if (part.output.type === "text") {
|
|
941
|
+
resultContent = part.output.value;
|
|
942
|
+
} else if (part.output.type === "json") {
|
|
943
|
+
resultContent = JSON.stringify(part.output.value);
|
|
944
|
+
} else if (part.output.type === "error-text") {
|
|
945
|
+
resultContent = part.output.value;
|
|
946
|
+
} else if (part.output.type === "error-json") {
|
|
947
|
+
resultContent = JSON.stringify(part.output.value);
|
|
948
|
+
} else {
|
|
949
|
+
resultContent = JSON.stringify(part.output);
|
|
950
|
+
}
|
|
951
|
+
items.push({
|
|
952
|
+
type: "function_call_output",
|
|
953
|
+
call_id: part.toolCallId,
|
|
954
|
+
output: resultContent
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return items;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Extract system instructions from prompt
|
|
964
|
+
*/
|
|
965
|
+
extractSystemInstructions(prompt) {
|
|
966
|
+
const systemMessages = prompt.filter((m) => m.role === "system").map((m) => m.content).join("\n");
|
|
967
|
+
return systemMessages || void 0;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Convert Responses API status to finish reason
|
|
971
|
+
* Note: Responses API returns 'completed' even when making tool calls,
|
|
972
|
+
* so we need to check the content for tool calls separately.
|
|
973
|
+
*/
|
|
974
|
+
convertResponsesStatus(status, hasToolCalls = false) {
|
|
975
|
+
if (hasToolCalls) {
|
|
976
|
+
return "tool-calls";
|
|
977
|
+
}
|
|
978
|
+
switch (status) {
|
|
979
|
+
case "completed":
|
|
980
|
+
return "stop";
|
|
981
|
+
case "incomplete":
|
|
982
|
+
return "length";
|
|
983
|
+
case "cancelled":
|
|
984
|
+
return "stop";
|
|
985
|
+
case "failed":
|
|
986
|
+
return "error";
|
|
987
|
+
default:
|
|
988
|
+
return "unknown";
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
async doGenerate(options) {
|
|
992
|
+
if (this.useResponsesApi) {
|
|
993
|
+
return this.doGenerateWithResponsesApi(options, false);
|
|
994
|
+
}
|
|
995
|
+
return this.doGenerateWithChatApi(options, false);
|
|
996
|
+
}
|
|
997
|
+
async doGenerateWithChatApi(options, isRetry) {
|
|
998
|
+
const client = await this.getOpenAIClient(isRetry);
|
|
999
|
+
const messages = this.convertPrompt(options.prompt);
|
|
1000
|
+
const tools = this.convertTools(options.tools);
|
|
1001
|
+
const toolChoice = options.toolChoice?.type !== "none" ? this.convertToolChoice(options.toolChoice) : void 0;
|
|
1002
|
+
const openaiModel = this.config.openaiModel || "gpt-4o";
|
|
1003
|
+
const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
|
|
1004
|
+
try {
|
|
1005
|
+
const response = await client.chat.completions.create({
|
|
1006
|
+
model: openaiModel,
|
|
1007
|
+
max_completion_tokens: maxTokens,
|
|
1008
|
+
messages,
|
|
1009
|
+
tools,
|
|
1010
|
+
tool_choice: tools ? toolChoice : void 0,
|
|
1011
|
+
temperature: options.temperature,
|
|
1012
|
+
top_p: options.topP,
|
|
1013
|
+
stop: options.stopSequences
|
|
1014
|
+
});
|
|
1015
|
+
const choice = response.choices[0];
|
|
1016
|
+
const content = [];
|
|
1017
|
+
if (choice?.message.content) {
|
|
1018
|
+
content.push({ type: "text", text: choice.message.content });
|
|
1019
|
+
}
|
|
1020
|
+
if (choice?.message.tool_calls) {
|
|
1021
|
+
for (const toolCall of choice.message.tool_calls) {
|
|
1022
|
+
if (toolCall.type === "function") {
|
|
1023
|
+
content.push({
|
|
1024
|
+
type: "tool-call",
|
|
1025
|
+
toolCallId: toolCall.id,
|
|
1026
|
+
toolName: toolCall.function.name,
|
|
1027
|
+
input: toolCall.function.arguments
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
const usage = {
|
|
1033
|
+
inputTokens: response.usage?.prompt_tokens || 0,
|
|
1034
|
+
outputTokens: response.usage?.completion_tokens || 0,
|
|
1035
|
+
totalTokens: response.usage?.total_tokens || 0
|
|
1036
|
+
};
|
|
1037
|
+
return {
|
|
1038
|
+
content,
|
|
1039
|
+
finishReason: this.convertFinishReason(choice?.finish_reason),
|
|
1040
|
+
usage,
|
|
1041
|
+
warnings: []
|
|
1042
|
+
};
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
if (!isRetry && this.isTokenError(error)) {
|
|
1045
|
+
this.directAccessClient.invalidateToken();
|
|
1046
|
+
return this.doGenerateWithChatApi(options, true);
|
|
1047
|
+
}
|
|
1048
|
+
if (error instanceof import_openai.default.APIError) {
|
|
1049
|
+
throw new GitLabError({
|
|
1050
|
+
message: `OpenAI API error: ${error.message}`,
|
|
1051
|
+
cause: error
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
throw error;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
async doGenerateWithResponsesApi(options, isRetry) {
|
|
1058
|
+
const client = await this.getOpenAIClient(isRetry);
|
|
1059
|
+
const input = this.convertPromptForResponses(options.prompt);
|
|
1060
|
+
const tools = this.convertToolsForResponses(options.tools);
|
|
1061
|
+
const instructions = this.extractSystemInstructions(options.prompt);
|
|
1062
|
+
const openaiModel = this.config.openaiModel || "gpt-5-codex";
|
|
1063
|
+
const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
|
|
1064
|
+
try {
|
|
1065
|
+
const response = await client.responses.create({
|
|
1066
|
+
model: openaiModel,
|
|
1067
|
+
input,
|
|
1068
|
+
instructions,
|
|
1069
|
+
tools,
|
|
1070
|
+
max_output_tokens: maxTokens,
|
|
1071
|
+
temperature: options.temperature,
|
|
1072
|
+
top_p: options.topP,
|
|
1073
|
+
store: false
|
|
1074
|
+
});
|
|
1075
|
+
const content = [];
|
|
1076
|
+
let hasToolCalls = false;
|
|
1077
|
+
for (const item of response.output || []) {
|
|
1078
|
+
if (item.type === "message" && item.role === "assistant") {
|
|
1079
|
+
for (const contentItem of item.content || []) {
|
|
1080
|
+
if (contentItem.type === "output_text") {
|
|
1081
|
+
content.push({ type: "text", text: contentItem.text });
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} else if (item.type === "function_call") {
|
|
1085
|
+
hasToolCalls = true;
|
|
1086
|
+
content.push({
|
|
1087
|
+
type: "tool-call",
|
|
1088
|
+
toolCallId: item.call_id,
|
|
1089
|
+
toolName: item.name,
|
|
1090
|
+
input: item.arguments
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
const usage = {
|
|
1095
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
1096
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
1097
|
+
totalTokens: response.usage?.total_tokens || 0
|
|
1098
|
+
};
|
|
1099
|
+
return {
|
|
1100
|
+
content,
|
|
1101
|
+
finishReason: this.convertResponsesStatus(response.status, hasToolCalls),
|
|
1102
|
+
usage,
|
|
1103
|
+
warnings: []
|
|
1104
|
+
};
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
if (!isRetry && this.isTokenError(error)) {
|
|
1107
|
+
this.directAccessClient.invalidateToken();
|
|
1108
|
+
return this.doGenerateWithResponsesApi(options, true);
|
|
1109
|
+
}
|
|
1110
|
+
if (error instanceof import_openai.default.APIError) {
|
|
1111
|
+
throw new GitLabError({
|
|
1112
|
+
message: `OpenAI API error: ${error.message}`,
|
|
1113
|
+
cause: error
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
throw error;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
async doStream(options) {
|
|
1120
|
+
if (this.useResponsesApi) {
|
|
1121
|
+
return this.doStreamWithResponsesApi(options, false);
|
|
1122
|
+
}
|
|
1123
|
+
return this.doStreamWithChatApi(options, false);
|
|
1124
|
+
}
|
|
1125
|
+
async doStreamWithChatApi(options, isRetry) {
|
|
1126
|
+
const client = await this.getOpenAIClient(isRetry);
|
|
1127
|
+
const messages = this.convertPrompt(options.prompt);
|
|
1128
|
+
const tools = this.convertTools(options.tools);
|
|
1129
|
+
const toolChoice = options.toolChoice?.type !== "none" ? this.convertToolChoice(options.toolChoice) : void 0;
|
|
1130
|
+
const openaiModel = this.config.openaiModel || "gpt-4o";
|
|
1131
|
+
const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
|
|
1132
|
+
const requestBody = {
|
|
1133
|
+
model: openaiModel,
|
|
1134
|
+
max_completion_tokens: maxTokens,
|
|
1135
|
+
messages,
|
|
1136
|
+
tools,
|
|
1137
|
+
tool_choice: tools ? toolChoice : void 0,
|
|
1138
|
+
temperature: options.temperature,
|
|
1139
|
+
top_p: options.topP,
|
|
1140
|
+
stop: options.stopSequences,
|
|
1141
|
+
stream: true,
|
|
1142
|
+
stream_options: { include_usage: true }
|
|
1143
|
+
};
|
|
1144
|
+
const self = this;
|
|
1145
|
+
const stream = new ReadableStream({
|
|
1146
|
+
start: async (controller) => {
|
|
1147
|
+
const toolCalls = {};
|
|
1148
|
+
const usage = {
|
|
1149
|
+
inputTokens: 0,
|
|
1150
|
+
outputTokens: 0,
|
|
1151
|
+
totalTokens: 0
|
|
1152
|
+
};
|
|
1153
|
+
let finishReason = "unknown";
|
|
1154
|
+
let textStarted = false;
|
|
1155
|
+
const textId = "text-0";
|
|
1156
|
+
try {
|
|
1157
|
+
const openaiStream = await client.chat.completions.create({
|
|
1158
|
+
...requestBody,
|
|
1159
|
+
stream: true
|
|
1160
|
+
});
|
|
1161
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
1162
|
+
for await (const chunk of openaiStream) {
|
|
1163
|
+
const choice = chunk.choices?.[0];
|
|
1164
|
+
if (chunk.id && !textStarted) {
|
|
1165
|
+
controller.enqueue({
|
|
1166
|
+
type: "response-metadata",
|
|
1167
|
+
id: chunk.id,
|
|
1168
|
+
modelId: chunk.model
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
if (choice?.delta?.content) {
|
|
1172
|
+
if (!textStarted) {
|
|
1173
|
+
controller.enqueue({ type: "text-start", id: textId });
|
|
1174
|
+
textStarted = true;
|
|
1175
|
+
}
|
|
1176
|
+
controller.enqueue({
|
|
1177
|
+
type: "text-delta",
|
|
1178
|
+
id: textId,
|
|
1179
|
+
delta: choice.delta.content
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
if (choice?.delta?.tool_calls) {
|
|
1183
|
+
for (const tc of choice.delta.tool_calls) {
|
|
1184
|
+
const idx = tc.index;
|
|
1185
|
+
if (!toolCalls[idx]) {
|
|
1186
|
+
toolCalls[idx] = {
|
|
1187
|
+
id: tc.id || "",
|
|
1188
|
+
name: tc.function?.name || "",
|
|
1189
|
+
arguments: ""
|
|
1190
|
+
};
|
|
1191
|
+
controller.enqueue({
|
|
1192
|
+
type: "tool-input-start",
|
|
1193
|
+
id: toolCalls[idx].id,
|
|
1194
|
+
toolName: toolCalls[idx].name
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
if (tc.function?.arguments) {
|
|
1198
|
+
toolCalls[idx].arguments += tc.function.arguments;
|
|
1199
|
+
controller.enqueue({
|
|
1200
|
+
type: "tool-input-delta",
|
|
1201
|
+
id: toolCalls[idx].id,
|
|
1202
|
+
delta: tc.function.arguments
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
if (choice?.finish_reason) {
|
|
1208
|
+
finishReason = self.convertFinishReason(choice.finish_reason);
|
|
1209
|
+
}
|
|
1210
|
+
if (chunk.usage) {
|
|
1211
|
+
usage.inputTokens = chunk.usage.prompt_tokens || 0;
|
|
1212
|
+
usage.outputTokens = chunk.usage.completion_tokens || 0;
|
|
1213
|
+
usage.totalTokens = chunk.usage.total_tokens || 0;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
if (textStarted) {
|
|
1217
|
+
controller.enqueue({ type: "text-end", id: textId });
|
|
1218
|
+
}
|
|
1219
|
+
for (const [, tc] of Object.entries(toolCalls)) {
|
|
1220
|
+
controller.enqueue({ type: "tool-input-end", id: tc.id });
|
|
1221
|
+
controller.enqueue({
|
|
1222
|
+
type: "tool-call",
|
|
1223
|
+
toolCallId: tc.id,
|
|
1224
|
+
toolName: tc.name,
|
|
1225
|
+
input: tc.arguments || "{}"
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
controller.enqueue({ type: "finish", finishReason, usage });
|
|
1229
|
+
controller.close();
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
if (!isRetry && self.isTokenError(error)) {
|
|
1232
|
+
self.directAccessClient.invalidateToken();
|
|
1233
|
+
controller.enqueue({
|
|
1234
|
+
type: "error",
|
|
1235
|
+
error: new GitLabError({ message: "TOKEN_REFRESH_NEEDED", cause: error })
|
|
1236
|
+
});
|
|
1237
|
+
controller.close();
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (error instanceof import_openai.default.APIError) {
|
|
1241
|
+
controller.enqueue({
|
|
1242
|
+
type: "error",
|
|
1243
|
+
error: new GitLabError({
|
|
1244
|
+
message: `OpenAI API error: ${error.message}`,
|
|
1245
|
+
cause: error
|
|
1246
|
+
})
|
|
1247
|
+
});
|
|
1248
|
+
} else {
|
|
1249
|
+
controller.enqueue({ type: "error", error });
|
|
1250
|
+
}
|
|
1251
|
+
controller.close();
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
return { stream, request: { body: requestBody } };
|
|
1256
|
+
}
|
|
1257
|
+
async doStreamWithResponsesApi(options, isRetry) {
|
|
1258
|
+
const client = await this.getOpenAIClient(isRetry);
|
|
1259
|
+
const input = this.convertPromptForResponses(options.prompt);
|
|
1260
|
+
const tools = this.convertToolsForResponses(options.tools);
|
|
1261
|
+
const instructions = this.extractSystemInstructions(options.prompt);
|
|
1262
|
+
const openaiModel = this.config.openaiModel || "gpt-5-codex";
|
|
1263
|
+
const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
|
|
1264
|
+
const requestBody = {
|
|
1265
|
+
model: openaiModel,
|
|
1266
|
+
input,
|
|
1267
|
+
instructions,
|
|
1268
|
+
tools,
|
|
1269
|
+
max_output_tokens: maxTokens,
|
|
1270
|
+
temperature: options.temperature,
|
|
1271
|
+
top_p: options.topP,
|
|
1272
|
+
store: false,
|
|
1273
|
+
stream: true
|
|
1274
|
+
};
|
|
1275
|
+
const self = this;
|
|
1276
|
+
const stream = new ReadableStream({
|
|
1277
|
+
start: async (controller) => {
|
|
1278
|
+
const toolCalls = {};
|
|
1279
|
+
const usage = {
|
|
1280
|
+
inputTokens: 0,
|
|
1281
|
+
outputTokens: 0,
|
|
1282
|
+
totalTokens: 0
|
|
1283
|
+
};
|
|
1284
|
+
let finishReason = "unknown";
|
|
1285
|
+
let textStarted = false;
|
|
1286
|
+
const textId = "text-0";
|
|
1287
|
+
try {
|
|
1288
|
+
const openaiStream = await client.responses.create({
|
|
1289
|
+
...requestBody,
|
|
1290
|
+
stream: true
|
|
1291
|
+
});
|
|
1292
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
1293
|
+
for await (const event of openaiStream) {
|
|
1294
|
+
if (event.type === "response.created") {
|
|
1295
|
+
controller.enqueue({
|
|
1296
|
+
type: "response-metadata",
|
|
1297
|
+
id: event.response.id,
|
|
1298
|
+
modelId: event.response.model
|
|
1299
|
+
});
|
|
1300
|
+
} else if (event.type === "response.output_item.added") {
|
|
1301
|
+
if (event.item.type === "function_call") {
|
|
1302
|
+
const outputIndex = event.output_index;
|
|
1303
|
+
const callId = event.item.call_id;
|
|
1304
|
+
toolCalls[outputIndex] = {
|
|
1305
|
+
callId,
|
|
1306
|
+
name: event.item.name,
|
|
1307
|
+
arguments: ""
|
|
1308
|
+
};
|
|
1309
|
+
controller.enqueue({
|
|
1310
|
+
type: "tool-input-start",
|
|
1311
|
+
id: callId,
|
|
1312
|
+
toolName: event.item.name
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
} else if (event.type === "response.output_text.delta") {
|
|
1316
|
+
if (!textStarted) {
|
|
1317
|
+
controller.enqueue({ type: "text-start", id: textId });
|
|
1318
|
+
textStarted = true;
|
|
1319
|
+
}
|
|
1320
|
+
controller.enqueue({
|
|
1321
|
+
type: "text-delta",
|
|
1322
|
+
id: textId,
|
|
1323
|
+
delta: event.delta
|
|
1324
|
+
});
|
|
1325
|
+
} else if (event.type === "response.function_call_arguments.delta") {
|
|
1326
|
+
const outputIndex = event.output_index;
|
|
1327
|
+
const tc = toolCalls[outputIndex];
|
|
1328
|
+
if (tc) {
|
|
1329
|
+
tc.arguments += event.delta;
|
|
1330
|
+
controller.enqueue({
|
|
1331
|
+
type: "tool-input-delta",
|
|
1332
|
+
id: tc.callId,
|
|
1333
|
+
delta: event.delta
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
} else if (event.type === "response.function_call_arguments.done") {
|
|
1337
|
+
const outputIndex = event.output_index;
|
|
1338
|
+
const tc = toolCalls[outputIndex];
|
|
1339
|
+
if (tc) {
|
|
1340
|
+
tc.arguments = event.arguments;
|
|
1341
|
+
}
|
|
1342
|
+
} else if (event.type === "response.completed") {
|
|
1343
|
+
const hasToolCalls2 = Object.keys(toolCalls).length > 0;
|
|
1344
|
+
finishReason = self.convertResponsesStatus(event.response.status, hasToolCalls2);
|
|
1345
|
+
if (event.response.usage) {
|
|
1346
|
+
usage.inputTokens = event.response.usage.input_tokens || 0;
|
|
1347
|
+
usage.outputTokens = event.response.usage.output_tokens || 0;
|
|
1348
|
+
usage.totalTokens = event.response.usage.total_tokens || 0;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (textStarted) {
|
|
1353
|
+
controller.enqueue({ type: "text-end", id: textId });
|
|
1354
|
+
}
|
|
1355
|
+
const hasToolCalls = Object.keys(toolCalls).length > 0;
|
|
1356
|
+
if (hasToolCalls && finishReason === "stop") {
|
|
1357
|
+
finishReason = "tool-calls";
|
|
1358
|
+
}
|
|
1359
|
+
for (const tc of Object.values(toolCalls)) {
|
|
1360
|
+
controller.enqueue({ type: "tool-input-end", id: tc.callId });
|
|
1361
|
+
controller.enqueue({
|
|
1362
|
+
type: "tool-call",
|
|
1363
|
+
toolCallId: tc.callId,
|
|
1364
|
+
toolName: tc.name,
|
|
1365
|
+
input: tc.arguments || "{}"
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
controller.enqueue({ type: "finish", finishReason, usage });
|
|
1369
|
+
controller.close();
|
|
1370
|
+
} catch (error) {
|
|
1371
|
+
if (!isRetry && self.isTokenError(error)) {
|
|
1372
|
+
self.directAccessClient.invalidateToken();
|
|
1373
|
+
controller.enqueue({
|
|
1374
|
+
type: "error",
|
|
1375
|
+
error: new GitLabError({ message: "TOKEN_REFRESH_NEEDED", cause: error })
|
|
1376
|
+
});
|
|
1377
|
+
controller.close();
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
if (error instanceof import_openai.default.APIError) {
|
|
1381
|
+
controller.enqueue({
|
|
1382
|
+
type: "error",
|
|
1383
|
+
error: new GitLabError({
|
|
1384
|
+
message: `OpenAI API error: ${error.message}`,
|
|
1385
|
+
cause: error
|
|
1386
|
+
})
|
|
1387
|
+
});
|
|
1388
|
+
} else {
|
|
1389
|
+
controller.enqueue({ type: "error", error });
|
|
1390
|
+
}
|
|
1391
|
+
controller.close();
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
return { stream, request: { body: requestBody } };
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
|
|
648
1399
|
// src/gitlab-oauth-types.ts
|
|
649
1400
|
var BUNDLED_CLIENT_ID = "36f2a70cddeb5a0889d4fd8295c241b7e9848e89cf9e599d0eed2d8e5350fbf5";
|
|
650
1401
|
var GITLAB_COM_URL = "https://gitlab.com";
|
|
@@ -807,16 +1558,6 @@ var GitLabOAuthManager = class {
|
|
|
807
1558
|
}
|
|
808
1559
|
};
|
|
809
1560
|
|
|
810
|
-
// src/model-mappings.ts
|
|
811
|
-
var MODEL_ID_TO_ANTHROPIC_MODEL = {
|
|
812
|
-
"duo-chat-opus-4-5": "claude-opus-4-5-20251101",
|
|
813
|
-
"duo-chat-sonnet-4-5": "claude-sonnet-4-5-20250929",
|
|
814
|
-
"duo-chat-haiku-4-5": "claude-haiku-4-5-20251001"
|
|
815
|
-
};
|
|
816
|
-
function getAnthropicModelForModelId(modelId) {
|
|
817
|
-
return MODEL_ID_TO_ANTHROPIC_MODEL[modelId];
|
|
818
|
-
}
|
|
819
|
-
|
|
820
1561
|
// src/gitlab-provider.ts
|
|
821
1562
|
var fs = __toESM(require("fs"));
|
|
822
1563
|
var path = __toESM(require("path"));
|
|
@@ -952,21 +1693,44 @@ function createGitLab(options = {}) {
|
|
|
952
1693
|
getApiKey().catch(() => {
|
|
953
1694
|
});
|
|
954
1695
|
const createAgenticChatModel = (modelId, agenticOptions) => {
|
|
1696
|
+
const mapping = getModelMapping(modelId);
|
|
1697
|
+
if (!mapping) {
|
|
1698
|
+
throw new GitLabError({
|
|
1699
|
+
message: `Unknown model ID: ${modelId}. Model must be registered in MODEL_MAPPINGS.`
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
if (agenticOptions?.providerModel) {
|
|
1703
|
+
const validModels = getValidModelsForProvider(mapping.provider);
|
|
1704
|
+
if (!validModels.includes(agenticOptions.providerModel)) {
|
|
1705
|
+
throw new GitLabError({
|
|
1706
|
+
message: `Invalid providerModel '${agenticOptions.providerModel}' for provider '${mapping.provider}'. Valid models: ${validModels.join(", ")}`
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
955
1710
|
const featureFlags = {
|
|
956
1711
|
DuoAgentPlatformNext: true,
|
|
957
1712
|
...options.featureFlags,
|
|
958
1713
|
...agenticOptions?.featureFlags
|
|
959
1714
|
};
|
|
960
|
-
|
|
1715
|
+
const baseConfig = {
|
|
961
1716
|
provider: `${providerName}.agentic`,
|
|
962
1717
|
instanceUrl,
|
|
963
1718
|
getHeaders,
|
|
964
1719
|
refreshApiKey,
|
|
965
1720
|
fetch: options.fetch,
|
|
966
|
-
anthropicModel: agenticOptions?.anthropicModel ?? getAnthropicModelForModelId(modelId),
|
|
967
1721
|
maxTokens: agenticOptions?.maxTokens,
|
|
968
1722
|
featureFlags,
|
|
969
1723
|
aiGatewayUrl: options.aiGatewayUrl
|
|
1724
|
+
};
|
|
1725
|
+
if (mapping.provider === "openai") {
|
|
1726
|
+
return new GitLabOpenAILanguageModel(modelId, {
|
|
1727
|
+
...baseConfig,
|
|
1728
|
+
openaiModel: agenticOptions?.providerModel ?? mapping.model
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
return new GitLabAnthropicLanguageModel(modelId, {
|
|
1732
|
+
...baseConfig,
|
|
1733
|
+
anthropicModel: agenticOptions?.providerModel ?? mapping.model
|
|
970
1734
|
});
|
|
971
1735
|
};
|
|
972
1736
|
const createDefaultModel = (modelId) => {
|
|
@@ -1266,17 +2030,25 @@ var GitLabProjectDetector = class {
|
|
|
1266
2030
|
BUNDLED_CLIENT_ID,
|
|
1267
2031
|
DEFAULT_AI_GATEWAY_URL,
|
|
1268
2032
|
GITLAB_COM_URL,
|
|
1269
|
-
|
|
2033
|
+
GitLabAnthropicLanguageModel,
|
|
1270
2034
|
GitLabDirectAccessClient,
|
|
1271
2035
|
GitLabError,
|
|
1272
2036
|
GitLabOAuthManager,
|
|
2037
|
+
GitLabOpenAILanguageModel,
|
|
1273
2038
|
GitLabProjectCache,
|
|
1274
2039
|
GitLabProjectDetector,
|
|
1275
2040
|
MODEL_ID_TO_ANTHROPIC_MODEL,
|
|
2041
|
+
MODEL_MAPPINGS,
|
|
1276
2042
|
OAUTH_SCOPES,
|
|
1277
2043
|
TOKEN_EXPIRY_SKEW_MS,
|
|
1278
2044
|
createGitLab,
|
|
1279
2045
|
getAnthropicModelForModelId,
|
|
1280
|
-
|
|
2046
|
+
getModelMapping,
|
|
2047
|
+
getOpenAIApiType,
|
|
2048
|
+
getOpenAIModelForModelId,
|
|
2049
|
+
getProviderForModelId,
|
|
2050
|
+
getValidModelsForProvider,
|
|
2051
|
+
gitlab,
|
|
2052
|
+
isResponsesApiModel
|
|
1281
2053
|
});
|
|
1282
2054
|
//# sourceMappingURL=index.js.map
|