@junando/core 0.14.0 → 0.15.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/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import { createHash, randomUUID } from "node:crypto";
6
6
  import pino from "pino";
7
7
  import { Readable, Writable } from "node:stream";
8
8
  import { DeleteItemCommand, DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
9
+ import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
10
+ import { GoogleGenerativeAI, GoogleGenerativeAIAbortError } from "@google/generative-ai";
9
11
  import * as Breaker from "opossum";
10
12
  import { readFileSync } from "node:fs";
11
13
  import { YAMLParseError, parse } from "yaml";
@@ -40,6 +42,7 @@ let LLMProviderType = /* @__PURE__ */ function(LLMProviderType) {
40
42
  LLMProviderType["Claude"] = "claude";
41
43
  LLMProviderType["OpenRouter"] = "openrouter";
42
44
  LLMProviderType["Qwen"] = "qwen";
45
+ LLMProviderType["Bedrock"] = "bedrock";
43
46
  return LLMProviderType;
44
47
  }({});
45
48
  const HTTP_TIMEOUT_MS = Object.freeze({
@@ -72,7 +75,8 @@ const LLM_FALLBACK_DEFAULTS = Object.freeze({
72
75
  const LLM_MODELS = Object.freeze({
73
76
  Gemini: "gemini-2.0-flash",
74
77
  Claude: "claude-haiku-4-5",
75
- OpenRouter: "qwen/qwen-2.5-72b-instruct"
78
+ OpenRouter: "qwen/qwen-2.5-72b-instruct",
79
+ Bedrock: "us.amazon.nova-lite-v1:0"
76
80
  });
77
81
  const SLACK_API_URL = "https://slack.com/api/chat.postMessage";
78
82
  const ROLLBACK_ACTION_ID = "trigger_rollback";
@@ -850,7 +854,7 @@ var ProcessIncidentUseCase = class {
850
854
  };
851
855
  //#endregion
852
856
  //#region src/infrastructure/dedup/redis-dedup.adapter.ts
853
- const logger$7 = createLogger();
857
+ const logger$8 = createLogger();
854
858
  var RedisDeduplicationStore = class {
855
859
  redis;
856
860
  keyPrefix = "junando:dedup:";
@@ -865,7 +869,7 @@ var RedisDeduplicationStore = class {
865
869
  };
866
870
  } catch (err) {
867
871
  const message = err instanceof Error ? err.message : String(err);
868
- logger$7.warn({
872
+ logger$8.warn({
869
873
  err,
870
874
  fingerprint
871
875
  }, "Redis dedup check failed, failing open");
@@ -905,7 +909,7 @@ var InMemoryDeduplicationStore = class {
905
909
  };
906
910
  //#endregion
907
911
  //#region src/infrastructure/dedup/dynamodb-dedup.adapter.ts
908
- const logger$6 = createLogger();
912
+ const logger$7 = createLogger();
909
913
  const CONDITION_EXPRESSION = "attribute_not_exists(fingerprint) OR expiresAt < :now";
910
914
  var DynamoDBDeduplicationStore = class {
911
915
  tableName;
@@ -941,7 +945,7 @@ var DynamoDBDeduplicationStore = class {
941
945
  ttlSeconds
942
946
  };
943
947
  const message = err instanceof Error ? err.message : String(err);
944
- logger$6.warn({
948
+ logger$7.warn({
945
949
  err,
946
950
  fingerprint
947
951
  }, "DynamoDB dedup check failed, failing open");
@@ -992,30 +996,8 @@ var InMemoryIndexer = class {
992
996
  }
993
997
  };
994
998
  //#endregion
995
- //#region src/infrastructure/llm/llm.adapter.ts
996
- const logger$5 = createLogger();
997
- /** Provider name reported by MockLLMProvider results. */
998
- const MOCK_PROVIDER_NAME = "mock";
999
- /**
1000
- * Schema for OpenRouter API response validation.
1001
- * Ensures type safety at the external boundary.
1002
- */
1003
- const OpenRouterResponseSchema = z.object({
1004
- id: z.string().optional(),
1005
- choices: z.array(z.object({
1006
- index: z.number(),
1007
- message: z.object({
1008
- role: z.string(),
1009
- content: z.string().optional()
1010
- }),
1011
- finish_reason: z.string().optional()
1012
- })),
1013
- usage: z.object({
1014
- prompt_tokens: z.number().optional(),
1015
- completion_tokens: z.number().optional(),
1016
- total_tokens: z.number().optional()
1017
- }).optional()
1018
- });
999
+ //#region src/infrastructure/llm/shared.ts
1000
+ const logger$6 = createLogger();
1019
1001
  const SYSTEM_PROMPT = `You are a senior Site Reliability Engineer.
1020
1002
  Respond ONLY with raw JSON, no markdown, no text before or after:
1021
1003
  {"probable_cause":"string","impacted_services":["string"],"recommended_steps":["string"],"urgency_level":"low|medium|high|critical","requires_rollback":true|false}`;
@@ -1029,11 +1011,6 @@ const RE_URGENCY_LEVEL = /"urgency_level"\s*:\s*"([^"]+)"/;
1029
1011
  const RE_REQUIRES_ROLLBACK = /"requires_rollback"\s*:\s*(true|false)/;
1030
1012
  const RE_RECOMMENDED_STEPS = /"recommended_steps"\s*:\s*\[([^\]]+)\]/;
1031
1013
  const RE_IMPACTED_SERVICES = /"impacted_services"\s*:\s*\[([^\]]+)\]/;
1032
- const BREAKER_OPTIONS = {
1033
- timeout: CIRCUIT_BREAKER.Timeout,
1034
- errorThresholdPercentage: CIRCUIT_BREAKER.ErrorThresholdPercentage,
1035
- resetTimeout: CIRCUIT_BREAKER.ResetTimeoutMs
1036
- };
1037
1014
  /**
1038
1015
  * Builds the user-facing prompt sent to the LLM for analysis.
1039
1016
  * Includes cluster summary and trace count for context.
@@ -1055,7 +1032,7 @@ function parseAnalysis(raw, correlationId) {
1055
1032
  stage1Succeeded = true;
1056
1033
  return analysis;
1057
1034
  } catch {}
1058
- if (!stage1Succeeded) logger$5.warn({
1035
+ if (!stage1Succeeded) logger$6.warn({
1059
1036
  rawResponse: raw.slice(0, 500),
1060
1037
  correlationId
1061
1038
  }, "llm:parse:failed");
@@ -1076,13 +1053,13 @@ function parseAnalysis(raw, correlationId) {
1076
1053
  requires_rollback: rollbackMatch?.[1] === "true"
1077
1054
  };
1078
1055
  const parsed = LLMAnalysisSchema.parse(analysis);
1079
- logger$5.warn({
1056
+ logger$6.warn({
1080
1057
  matchedFields: ["probable_cause", "urgency_level"],
1081
1058
  correlationId
1082
1059
  }, "llm:parse:partial");
1083
1060
  return parsed;
1084
1061
  }
1085
- logger$5.warn({ correlationId }, "llm:parse:unusable");
1062
+ logger$6.warn({ correlationId }, "llm:parse:unusable");
1086
1063
  return null;
1087
1064
  }
1088
1065
  /**
@@ -1092,7 +1069,7 @@ function parseAnalysis(raw, correlationId) {
1092
1069
  */
1093
1070
  function parseLlmText(raw, correlationId) {
1094
1071
  if (raw.trim() === "") {
1095
- logger$5.warn({ correlationId }, "llm:parse:empty");
1072
+ logger$6.warn({ correlationId }, "llm:parse:empty");
1096
1073
  return {
1097
1074
  analysis: null,
1098
1075
  degradedReason: "empty_response"
@@ -1104,49 +1081,79 @@ function parseLlmText(raw, correlationId) {
1104
1081
  degradedReason: "unparseable_response"
1105
1082
  };
1106
1083
  }
1084
+ //#endregion
1085
+ //#region src/infrastructure/llm/bedrock.provider.ts
1107
1086
  /**
1108
- * Gemini LLM provider using Google Generative AI SDK.
1109
- * Wrapped with circuit breaker for resilience.
1087
+ * Recognized transient Bedrock error names, mapped to their degradedReason.
1088
+ * Any other error name (or non-Error rejection) is rethrown by the caller.
1110
1089
  */
1111
- var GeminiProvider = class {
1112
- apiKey;
1090
+ const BEDROCK_AVAILABILITY_ERRORS = /* @__PURE__ */ new Map([
1091
+ ["ThrottlingException", "provider_unavailable"],
1092
+ ["ServiceUnavailableException", "provider_unavailable"],
1093
+ ["InternalServerException", "provider_unavailable"],
1094
+ ["ModelTimeoutException", "timeout"],
1095
+ ["AbortError", "timeout"]
1096
+ ]);
1097
+ function classifyBedrockError(error) {
1098
+ if (!(error instanceof Error)) return void 0;
1099
+ return BEDROCK_AVAILABILITY_ERRORS.get(error.name);
1100
+ }
1101
+ /**
1102
+ * Bedrock LLM provider using AWS Bedrock Runtime's Converse API.
1103
+ * No circuit breaker — Bedrock's own throttling/timeout errors are
1104
+ * classified directly into a degradedReason.
1105
+ */
1106
+ var BedrockProvider = class {
1113
1107
  model;
1114
- breaker;
1115
- constructor(apiKey, model = LLM_MODELS.Gemini) {
1116
- this.apiKey = apiKey;
1108
+ client = null;
1109
+ constructor(model = LLM_MODELS.Bedrock) {
1117
1110
  this.model = model;
1118
- this.breaker = new Breaker.default(this.analyzeRaw.bind(this), BREAKER_OPTIONS);
1111
+ }
1112
+ getClient() {
1113
+ if (!this.client) this.client = new BedrockRuntimeClient({});
1114
+ return this.client;
1119
1115
  }
1120
1116
  async analyze(cluster, traces) {
1121
1117
  const startMs = Date.now();
1122
- return {
1123
- ...await this.analyzeWithBreaker(cluster, traces),
1124
- provider: "gemini",
1125
- model: this.model,
1126
- latencyMs: Date.now() - startMs
1127
- };
1128
- }
1129
- async analyzeWithBreaker(cluster, traces) {
1118
+ const abortController = new AbortController();
1119
+ const timeoutHandle = setTimeout(() => abortController.abort(), HTTP_TIMEOUT_MS.LLM);
1130
1120
  try {
1131
- return await this.breaker.fire(cluster, traces);
1132
- } catch {
1133
- return this.analyzeRaw(cluster, traces);
1121
+ const response = await this.getClient().send(new ConverseCommand({
1122
+ modelId: this.model,
1123
+ system: [{ text: SYSTEM_PROMPT }],
1124
+ messages: [{
1125
+ role: "user",
1126
+ content: [{ text: buildUserPrompt(cluster, traces) }]
1127
+ }],
1128
+ inferenceConfig: { maxTokens: LLM_MAX_TOKENS }
1129
+ }), { abortSignal: abortController.signal });
1130
+ return {
1131
+ ...parseLlmText(response.output?.message?.content?.find((b) => b.text !== void 0)?.text ?? ""),
1132
+ provider: "bedrock",
1133
+ model: this.model,
1134
+ latencyMs: Date.now() - startMs,
1135
+ promptTokens: response.usage?.inputTokens ?? 0,
1136
+ completionTokens: response.usage?.outputTokens ?? 0
1137
+ };
1138
+ } catch (error) {
1139
+ const degradedReason = classifyBedrockError(error);
1140
+ if (degradedReason === void 0) throw error;
1141
+ return {
1142
+ analysis: null,
1143
+ degradedReason,
1144
+ provider: "bedrock",
1145
+ model: this.model,
1146
+ latencyMs: Date.now() - startMs,
1147
+ promptTokens: 0,
1148
+ completionTokens: 0
1149
+ };
1150
+ } finally {
1151
+ clearTimeout(timeoutHandle);
1134
1152
  }
1135
1153
  }
1136
- async analyzeRaw(cluster, traces) {
1137
- const { GoogleGenerativeAI } = await import("@google/generative-ai");
1138
- const result = await new GoogleGenerativeAI(this.apiKey).getGenerativeModel({
1139
- model: this.model,
1140
- systemInstruction: SYSTEM_PROMPT
1141
- }).generateContent(buildUserPrompt(cluster, traces));
1142
- const usage = result.response.usageMetadata;
1143
- return {
1144
- ...parseLlmText(result.response.text()),
1145
- promptTokens: usage?.promptTokenCount ?? 0,
1146
- completionTokens: usage?.candidatesTokenCount ?? 0
1147
- };
1148
- }
1149
1154
  };
1155
+ //#endregion
1156
+ //#region src/infrastructure/llm/claude.provider.ts
1150
1157
  /**
1151
1158
  * Claude LLM provider using Anthropic SDK.
1152
1159
  * Supports Claude Haiku and other models.
@@ -1180,30 +1187,108 @@ var ClaudeProvider = class {
1180
1187
  };
1181
1188
  }
1182
1189
  };
1190
+ //#endregion
1191
+ //#region src/infrastructure/llm/gemini.provider.ts
1192
+ const OPOSSUM_TIMEOUT_CODE = "ETIMEDOUT";
1193
+ const OPOSSUM_OPEN_BREAKER_CODE = "EOPENBREAKER";
1194
+ const UNDICI_CONNECT_TIMEOUT_CODE = "UND_ERR_CONNECT_TIMEOUT";
1195
+ function isErrorLike(value) {
1196
+ return typeof value === "object" && value !== null;
1197
+ }
1198
+ function hasTimeoutCode(error) {
1199
+ return error.code === OPOSSUM_TIMEOUT_CODE || error.code === UNDICI_CONNECT_TIMEOUT_CODE;
1200
+ }
1201
+ function isStandardFetchTimeout(error) {
1202
+ return error instanceof DOMException && error.name === "TimeoutError";
1203
+ }
1204
+ function classifyGeminiAvailabilityError(error) {
1205
+ if (!isErrorLike(error)) return void 0;
1206
+ if (error.code === OPOSSUM_OPEN_BREAKER_CODE) return "circuit_breaker_open";
1207
+ if (hasTimeoutCode(error)) return "timeout";
1208
+ if (error instanceof GoogleGenerativeAIAbortError) return "timeout";
1209
+ if (isStandardFetchTimeout(error)) return "timeout";
1210
+ if (isErrorLike(error.cause) && (hasTimeoutCode(error.cause) || isStandardFetchTimeout(error.cause))) return "timeout";
1211
+ }
1212
+ function degradedGeminiResult(degradedReason) {
1213
+ return {
1214
+ analysis: null,
1215
+ degradedReason,
1216
+ promptTokens: 0,
1217
+ completionTokens: 0
1218
+ };
1219
+ }
1220
+ const BREAKER_OPTIONS = {
1221
+ timeout: CIRCUIT_BREAKER.Timeout,
1222
+ errorThresholdPercentage: CIRCUIT_BREAKER.ErrorThresholdPercentage,
1223
+ resetTimeout: CIRCUIT_BREAKER.ResetTimeoutMs
1224
+ };
1183
1225
  /**
1184
- * Mock LLM provider for testing and local development.
1185
- * Returns deterministic responses without external API calls.
1226
+ * Gemini LLM provider using Google Generative AI SDK.
1227
+ * Wrapped with circuit breaker for resilience.
1186
1228
  */
1187
- var MockLLMProvider = class {
1188
- callLog = [];
1189
- async analyze(cluster, _traces) {
1190
- this.callLog.push({ cluster });
1229
+ var GeminiProvider = class {
1230
+ apiKey;
1231
+ model;
1232
+ breaker;
1233
+ constructor(apiKey, model = LLM_MODELS.Gemini) {
1234
+ this.apiKey = apiKey;
1235
+ this.model = model;
1236
+ this.breaker = new Breaker.default(this.analyzeRaw.bind(this), BREAKER_OPTIONS);
1237
+ }
1238
+ async analyze(cluster, traces) {
1239
+ const startMs = Date.now();
1191
1240
  return {
1192
- analysis: {
1193
- probable_cause: `Mock: ${cluster.alertType} on ${cluster.serviceName}`,
1194
- impacted_services: [cluster.serviceName],
1195
- recommended_steps: ["Check the logs", "Verify the deployment"],
1196
- urgency_level: "high",
1197
- requires_rollback: false
1198
- },
1199
- provider: MOCK_PROVIDER_NAME,
1200
- model: MOCK_PROVIDER_NAME,
1201
- latencyMs: 0,
1202
- promptTokens: 0,
1203
- completionTokens: 0
1241
+ ...await this.analyzeWithBreaker(cluster, traces),
1242
+ provider: "gemini",
1243
+ model: this.model,
1244
+ latencyMs: Date.now() - startMs
1245
+ };
1246
+ }
1247
+ async analyzeWithBreaker(cluster, traces) {
1248
+ try {
1249
+ return await this.breaker.fire(cluster, traces);
1250
+ } catch (error) {
1251
+ const degradedReason = classifyGeminiAvailabilityError(error);
1252
+ if (degradedReason !== void 0) return degradedGeminiResult(degradedReason);
1253
+ throw error;
1254
+ }
1255
+ }
1256
+ async analyzeRaw(cluster, traces) {
1257
+ const result = await new GoogleGenerativeAI(this.apiKey).getGenerativeModel({
1258
+ model: this.model,
1259
+ systemInstruction: SYSTEM_PROMPT
1260
+ }).generateContent(buildUserPrompt(cluster, traces));
1261
+ const usage = result.response.usageMetadata;
1262
+ return {
1263
+ ...parseLlmText(result.response.text()),
1264
+ promptTokens: usage?.promptTokenCount ?? 0,
1265
+ completionTokens: usage?.candidatesTokenCount ?? 0
1204
1266
  };
1205
1267
  }
1206
1268
  };
1269
+ //#endregion
1270
+ //#region src/infrastructure/llm/openrouter.provider.ts
1271
+ const logger$5 = createLogger();
1272
+ /**
1273
+ * Schema for OpenRouter API response validation.
1274
+ * Ensures type safety at the external boundary.
1275
+ */
1276
+ const OpenRouterResponseSchema = z.object({
1277
+ id: z.string().optional(),
1278
+ choices: z.array(z.object({
1279
+ index: z.number(),
1280
+ message: z.object({
1281
+ role: z.string(),
1282
+ content: z.string().optional()
1283
+ }),
1284
+ finish_reason: z.string().optional()
1285
+ })),
1286
+ usage: z.object({
1287
+ prompt_tokens: z.number().optional(),
1288
+ completion_tokens: z.number().optional(),
1289
+ total_tokens: z.number().optional()
1290
+ }).optional()
1291
+ });
1207
1292
  /**
1208
1293
  * OpenRouter LLM provider using OpenAI-compatible API.
1209
1294
  * Supports various open models (Qwen, etc.) via OpenRouter gateway.
@@ -1367,6 +1452,8 @@ var OpenRouterProvider = class {
1367
1452
  throw new Error("OpenRouter API exhausted all models");
1368
1453
  }
1369
1454
  };
1455
+ //#endregion
1456
+ //#region src/infrastructure/llm/factory.ts
1370
1457
  /**
1371
1458
  * Registry mapping provider names to their factory functions.
1372
1459
  * Used by createLLMProvider to instantiate the appropriate LLM client.
@@ -1375,7 +1462,8 @@ const LLM_PROVIDER_REGISTRY = /* @__PURE__ */ new Map([
1375
1462
  ["gemini", (apiKey, model) => new GeminiProvider(apiKey, model)],
1376
1463
  ["claude", (apiKey, model) => new ClaudeProvider(apiKey, model)],
1377
1464
  ["openrouter", (apiKey, model, options) => new OpenRouterProvider(apiKey, model, options?.fallbackModels, options?.fallbackTimeoutMs, "openrouter")],
1378
- ["qwen", (apiKey, model, options) => new OpenRouterProvider(apiKey, model, options?.fallbackModels, options?.fallbackTimeoutMs, "qwen")]
1465
+ ["qwen", (apiKey, model, options) => new OpenRouterProvider(apiKey, model, options?.fallbackModels, options?.fallbackTimeoutMs, "qwen")],
1466
+ ["bedrock", (_apiKey, model) => new BedrockProvider(model)]
1379
1467
  ]);
1380
1468
  function createLLMProvider(provider, apiKey, model, options) {
1381
1469
  const factory = LLM_PROVIDER_REGISTRY.get(provider);
@@ -1383,9 +1471,37 @@ function createLLMProvider(provider, apiKey, model, options) {
1383
1471
  const supported = Array.from(LLM_PROVIDER_REGISTRY.keys()).join(", ");
1384
1472
  throw new Error(`Unknown LLM_PROVIDER: "${provider}". Supported: ${supported}`);
1385
1473
  }
1386
- return factory(apiKey, model, options);
1474
+ return factory(apiKey ?? "", model, options);
1387
1475
  }
1388
1476
  //#endregion
1477
+ //#region src/infrastructure/llm/mock.provider.ts
1478
+ /** Provider name reported by MockLLMProvider results. */
1479
+ const MOCK_PROVIDER_NAME = "mock";
1480
+ /**
1481
+ * Mock LLM provider for testing and local development.
1482
+ * Returns deterministic responses without external API calls.
1483
+ */
1484
+ var MockLLMProvider = class {
1485
+ callLog = [];
1486
+ async analyze(cluster, _traces) {
1487
+ this.callLog.push({ cluster });
1488
+ return {
1489
+ analysis: {
1490
+ probable_cause: `Mock: ${cluster.alertType} on ${cluster.serviceName}`,
1491
+ impacted_services: [cluster.serviceName],
1492
+ recommended_steps: ["Check the logs", "Verify the deployment"],
1493
+ urgency_level: "high",
1494
+ requires_rollback: false
1495
+ },
1496
+ provider: MOCK_PROVIDER_NAME,
1497
+ model: MOCK_PROVIDER_NAME,
1498
+ latencyMs: 0,
1499
+ promptTokens: 0,
1500
+ completionTokens: 0
1501
+ };
1502
+ }
1503
+ };
1504
+ //#endregion
1389
1505
  //#region src/shared/factory-registry.ts
1390
1506
  var FactoryRegistry = class {
1391
1507
  _factories = /* @__PURE__ */ new Map();
@@ -35696,6 +35812,20 @@ function parseBooleanEnv(value) {
35696
35812
  const normalized = value.trim().toLowerCase();
35697
35813
  return normalized === "true" || normalized === "1";
35698
35814
  }
35815
+ function llmApiKeyRequired(llmProvider, llmApiKey) {
35816
+ return llmProvider !== "bedrock" && !llmApiKey;
35817
+ }
35818
+ /**
35819
+ * Resolves the effective LLM_MODEL. An explicit LLM_MODEL always wins, for any
35820
+ * provider. BEDROCK_DEFAULT_MODEL (set by the CDK stack from the deployment
35821
+ * region — see junando-stack.ts) is used ONLY as a fallback when the provider
35822
+ * is bedrock, so a Bedrock-specific value can never leak into another
35823
+ * provider's model override.
35824
+ */
35825
+ function resolveLlmModel(llmProvider, llmModel, bedrockDefaultModel) {
35826
+ if (llmModel) return llmModel;
35827
+ return llmProvider === "bedrock" ? bedrockDefaultModel : void 0;
35828
+ }
35699
35829
  function parseOptionalStringArray(value) {
35700
35830
  if (value === void 0 || value === "") return void 0;
35701
35831
  return value.split(",").map((s) => s.trim()).filter(Boolean);
@@ -35745,9 +35875,10 @@ const ConfigSchema = z.object({
35745
35875
  "gemini",
35746
35876
  "claude",
35747
35877
  "openrouter",
35748
- "qwen"
35878
+ "qwen",
35879
+ "bedrock"
35749
35880
  ]),
35750
- llmApiKey: z.string().min(1),
35881
+ llmApiKey: z.string().min(1).optional(),
35751
35882
  llmModel: z.string().optional().transform((v) => v === "" ? void 0 : v),
35752
35883
  notifierType: z.enum(["slack", "teams"]).default("slack"),
35753
35884
  slackBotToken: z.string().startsWith("xoxb-").optional(),
@@ -35830,13 +35961,18 @@ const ConfigSchema = z.object({
35830
35961
  path: ["redisUrl"],
35831
35962
  message: "[dedupStore: redis] REDIS_URL is required"
35832
35963
  });
35964
+ if (llmApiKeyRequired(data.llmProvider, data.llmApiKey)) ctx.addIssue({
35965
+ code: z.ZodIssueCode.custom,
35966
+ path: ["llmApiKey"],
35967
+ message: `[llmProvider: ${data.llmProvider}] LLM_API_KEY is required`
35968
+ });
35833
35969
  });
35834
35970
  async function loadConfig() {
35835
35971
  await loadSecretsFromSSM();
35836
35972
  const result = ConfigSchema.safeParse({
35837
35973
  llmProvider: process.env["LLM_PROVIDER"],
35838
35974
  llmApiKey: process.env["LLM_API_KEY"],
35839
- llmModel: process.env["LLM_MODEL"],
35975
+ llmModel: resolveLlmModel(process.env["LLM_PROVIDER"], process.env["LLM_MODEL"], process.env["BEDROCK_DEFAULT_MODEL"]),
35840
35976
  notifierType: process.env["NOTIFIER_TYPE"],
35841
35977
  slackBotToken: process.env["SLACK_BOT_TOKEN"],
35842
35978
  slackSigningSecret: process.env["SLACK_SIGNING_SECRET"],
@@ -35859,11 +35995,14 @@ async function loadConfig() {
35859
35995
  });
35860
35996
  if (!result.success) {
35861
35997
  const errorMessages = result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`);
35998
+ const rawLlmProvider = process.env["LLM_PROVIDER"];
35999
+ const rawLlmApiKey = process.env["LLM_API_KEY"];
36000
+ if (!errorMessages.some((m) => m.startsWith("llmApiKey")) && llmApiKeyRequired(rawLlmProvider, rawLlmApiKey)) errorMessages.push(`llmApiKey: [llmProvider: ${rawLlmProvider ?? "(unset)"}] LLM_API_KEY is required`);
35862
36001
  throw new Error(`Invalid configuration:\n - ${errorMessages.join("\n - ")}`);
35863
36002
  }
35864
36003
  return result.data;
35865
36004
  }
35866
36005
  //#endregion
35867
- export { ALERT_TYPE_LABELS, AlertClusterSchema, AlertStatusSchema, AlertType, AlertmanagerPayloadSchema, CIRCUIT_BREAKER, ChannelRegistry, ClaudeProvider, ClusteringService, Component, ConsoleNotifier, DEDUP_TTL_MS_MULTIPLIER, DEV_SERVER_PORT, DynamoDBDeduplicationStore, FactoryRegistry, Fingerprint, GeminiProvider, HOUR_MS, HTTP_TIMEOUT_MS, InMemoryAlertQueue, InMemoryDeduplicationStore, InMemoryIndexer, IncidentSchema, LLMAnalysisSchema, LLMProviderType, LLM_FALLBACK_DEFAULTS, LLM_MAX_TOKENS, LLM_MODELS, LokiTraceRepository, MockLLMProvider, MockTraceRepository, NoopRollbackActionHandler, NormalizedAlertSchema, NotifyOutcome, OpenSearchIndexer, Outcome, PAYLOAD_DEFAULTS, ProcessIncidentUseCase, RATE_LIMITER, REDIS_KEY_PREFIX, ROLLBACK_ACTION_ID, RedisDeduplicationStore, RoutingNotifier, RuleActionSchema, RuleActionType, RuleConditionSchema, RuleConfigurationSchema, RuleEngine, RuleEvaluationPhase, RuleSchema, RuleSectionSchema, SLACK_API_URL, SQSAlertQueue, SamplingDecision, SeverityLevel, SlackNotifier, Stage, TEAMS_WEBHOOK_TIMEOUT_MS, TeamsNotifier, TeamsNotifierError, URGENCY_EMOJI, UrgencyLevelSchema, WEBHOOK_DEFAULTS, WideEventBuilder, compileCondition, createLLMProvider, createLogger, createNotifier, createRollbackActionHandler, dispatchActions, flushLoki, loadConfig, metrics_exports as metrics, normalizePayload, parseRuleConfig, reinitLogger, startSqsLagPoller };
36006
+ export { ALERT_TYPE_LABELS, AlertClusterSchema, AlertStatusSchema, AlertType, AlertmanagerPayloadSchema, BedrockProvider, CIRCUIT_BREAKER, ChannelRegistry, ClaudeProvider, ClusteringService, Component, ConsoleNotifier, DEDUP_TTL_MS_MULTIPLIER, DEV_SERVER_PORT, DynamoDBDeduplicationStore, FactoryRegistry, Fingerprint, GeminiProvider, HOUR_MS, HTTP_TIMEOUT_MS, InMemoryAlertQueue, InMemoryDeduplicationStore, InMemoryIndexer, IncidentSchema, LLMAnalysisSchema, LLMProviderType, LLM_FALLBACK_DEFAULTS, LLM_MAX_TOKENS, LLM_MODELS, LokiTraceRepository, MockLLMProvider, MockTraceRepository, NoopRollbackActionHandler, NormalizedAlertSchema, NotifyOutcome, OpenRouterProvider, OpenSearchIndexer, Outcome, PAYLOAD_DEFAULTS, ProcessIncidentUseCase, RATE_LIMITER, REDIS_KEY_PREFIX, ROLLBACK_ACTION_ID, RedisDeduplicationStore, RoutingNotifier, RuleActionSchema, RuleActionType, RuleConditionSchema, RuleConfigurationSchema, RuleEngine, RuleEvaluationPhase, RuleSchema, RuleSectionSchema, SLACK_API_URL, SQSAlertQueue, SamplingDecision, SeverityLevel, SlackNotifier, Stage, TEAMS_WEBHOOK_TIMEOUT_MS, TeamsNotifier, TeamsNotifierError, URGENCY_EMOJI, UrgencyLevelSchema, WEBHOOK_DEFAULTS, WideEventBuilder, compileCondition, createLLMProvider, createLogger, createNotifier, createRollbackActionHandler, dispatchActions, flushLoki, loadConfig, metrics_exports as metrics, normalizePayload, parseRuleConfig, reinitLogger, startSqsLagPoller };
35868
36007
 
35869
36008
  //# sourceMappingURL=index.js.map