@junando/worker 0.12.3 → 0.13.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/handler.cjs +113 -86
- package/package.json +2 -2
package/dist/handler.cjs
CHANGED
|
@@ -24309,6 +24309,59 @@ var WideEventBuilder = class {
|
|
|
24309
24309
|
}
|
|
24310
24310
|
};
|
|
24311
24311
|
|
|
24312
|
+
//#endregion
|
|
24313
|
+
//#region ../core/src/shared/logger/enums.ts
|
|
24314
|
+
/**
|
|
24315
|
+
* Pipeline component taxonomy.
|
|
24316
|
+
*
|
|
24317
|
+
* `component` (not `service`) distinguishes pipeline stages in wide events.
|
|
24318
|
+
* `service` stays constant ("junando"); `component` tells you WHERE the event
|
|
24319
|
+
* was emitted from.
|
|
24320
|
+
*/
|
|
24321
|
+
const Component = {
|
|
24322
|
+
Webhook: "webhook",
|
|
24323
|
+
Worker: "worker",
|
|
24324
|
+
UseCase: "useCase",
|
|
24325
|
+
Llm: "llm",
|
|
24326
|
+
Notifier: "notifier",
|
|
24327
|
+
Dedup: "dedup",
|
|
24328
|
+
Traces: "traces",
|
|
24329
|
+
Ingest: "ingest",
|
|
24330
|
+
Rollback: "rollback"
|
|
24331
|
+
};
|
|
24332
|
+
/**
|
|
24333
|
+
* Pipeline stages that write their results into the WideEventBuilder.
|
|
24334
|
+
*/
|
|
24335
|
+
const Stage = {
|
|
24336
|
+
Dedup: "dedup",
|
|
24337
|
+
RulesPre: "rulesPre",
|
|
24338
|
+
Traces: "traces",
|
|
24339
|
+
Llm: "llm",
|
|
24340
|
+
RulesPost: "rulesPost",
|
|
24341
|
+
Notify: "notify"
|
|
24342
|
+
};
|
|
24343
|
+
/**
|
|
24344
|
+
* Terminal outcomes for a wide event across all entry points.
|
|
24345
|
+
*/
|
|
24346
|
+
const Outcome = {
|
|
24347
|
+
Success: "success",
|
|
24348
|
+
Suppressed: "suppressed",
|
|
24349
|
+
Degraded: "degraded",
|
|
24350
|
+
Error: "error",
|
|
24351
|
+
Accepted: "accepted",
|
|
24352
|
+
Empty: "empty",
|
|
24353
|
+
ParseError: "parse_error"
|
|
24354
|
+
};
|
|
24355
|
+
/**
|
|
24356
|
+
* Reason recorded for a tail-sampling decision.
|
|
24357
|
+
*/
|
|
24358
|
+
const SamplingDecision$1 = {
|
|
24359
|
+
Error: "error",
|
|
24360
|
+
Slow: "slow",
|
|
24361
|
+
Random: "random",
|
|
24362
|
+
Skipped: "skipped"
|
|
24363
|
+
};
|
|
24364
|
+
|
|
24312
24365
|
//#endregion
|
|
24313
24366
|
//#region ../core/src/shared/logger/sampling.ts
|
|
24314
24367
|
/** Events slower than this (ms) are always sampled. */
|
|
@@ -24328,6 +24381,9 @@ function shouldSample(event) {
|
|
|
24328
24381
|
if (event.durationMs !== undefined && event.durationMs > 1e4) {
|
|
24329
24382
|
return true;
|
|
24330
24383
|
}
|
|
24384
|
+
if (event.outcome === Outcome.Degraded) {
|
|
24385
|
+
return true;
|
|
24386
|
+
}
|
|
24331
24387
|
return Math.random() < NORMAL_SAMPLE_RATE;
|
|
24332
24388
|
}
|
|
24333
24389
|
|
|
@@ -24416,59 +24472,6 @@ function redact(obj) {
|
|
|
24416
24472
|
}));
|
|
24417
24473
|
}
|
|
24418
24474
|
|
|
24419
|
-
//#endregion
|
|
24420
|
-
//#region ../core/src/shared/logger/enums.ts
|
|
24421
|
-
/**
|
|
24422
|
-
* Pipeline component taxonomy.
|
|
24423
|
-
*
|
|
24424
|
-
* `component` (not `service`) distinguishes pipeline stages in wide events.
|
|
24425
|
-
* `service` stays constant ("junando"); `component` tells you WHERE the event
|
|
24426
|
-
* was emitted from.
|
|
24427
|
-
*/
|
|
24428
|
-
const Component = {
|
|
24429
|
-
Webhook: "webhook",
|
|
24430
|
-
Worker: "worker",
|
|
24431
|
-
UseCase: "useCase",
|
|
24432
|
-
Llm: "llm",
|
|
24433
|
-
Notifier: "notifier",
|
|
24434
|
-
Dedup: "dedup",
|
|
24435
|
-
Traces: "traces",
|
|
24436
|
-
Ingest: "ingest",
|
|
24437
|
-
Rollback: "rollback"
|
|
24438
|
-
};
|
|
24439
|
-
/**
|
|
24440
|
-
* Pipeline stages that write their results into the WideEventBuilder.
|
|
24441
|
-
*/
|
|
24442
|
-
const Stage = {
|
|
24443
|
-
Dedup: "dedup",
|
|
24444
|
-
RulesPre: "rulesPre",
|
|
24445
|
-
Traces: "traces",
|
|
24446
|
-
Llm: "llm",
|
|
24447
|
-
RulesPost: "rulesPost",
|
|
24448
|
-
Notify: "notify"
|
|
24449
|
-
};
|
|
24450
|
-
/**
|
|
24451
|
-
* Terminal outcomes for a wide event across all entry points.
|
|
24452
|
-
*/
|
|
24453
|
-
const Outcome = {
|
|
24454
|
-
Success: "success",
|
|
24455
|
-
Suppressed: "suppressed",
|
|
24456
|
-
Degraded: "degraded",
|
|
24457
|
-
Error: "error",
|
|
24458
|
-
Accepted: "accepted",
|
|
24459
|
-
Empty: "empty",
|
|
24460
|
-
ParseError: "parse_error"
|
|
24461
|
-
};
|
|
24462
|
-
/**
|
|
24463
|
-
* Reason recorded for a tail-sampling decision.
|
|
24464
|
-
*/
|
|
24465
|
-
const SamplingDecision$1 = {
|
|
24466
|
-
Error: "error",
|
|
24467
|
-
Slow: "slow",
|
|
24468
|
-
Random: "random",
|
|
24469
|
-
Skipped: "skipped"
|
|
24470
|
-
};
|
|
24471
|
-
|
|
24472
24475
|
//#endregion
|
|
24473
24476
|
//#region ../core/src/shared/logger/index.ts
|
|
24474
24477
|
let _root = buildLogger({});
|
|
@@ -29393,12 +29396,14 @@ function toErrorSection(err) {
|
|
|
29393
29396
|
}
|
|
29394
29397
|
/**
|
|
29395
29398
|
* Terminal outcome for a processed cluster. Early returns — no switch/case.
|
|
29396
|
-
* Notify failure is fatal (the batch is retried via SQS); LLM
|
|
29397
|
-
* degraded
|
|
29399
|
+
* Notify failure is fatal (the batch is retried via SQS); LLM transport
|
|
29400
|
+
* failure and a parse-degraded diagnosis both leave the cluster degraded
|
|
29401
|
+
* (notification still went out, with or without a diagnosis).
|
|
29398
29402
|
*/
|
|
29399
|
-
function resolveOutcome({ llmError, notifyError }) {
|
|
29403
|
+
function resolveOutcome({ llmError, notifyError, llmDegradedReason }) {
|
|
29400
29404
|
if (notifyError != null) return Outcome.Error;
|
|
29401
29405
|
if (llmError != null) return Outcome.Degraded;
|
|
29406
|
+
if (llmDegradedReason != null) return Outcome.Degraded;
|
|
29402
29407
|
return Outcome.Success;
|
|
29403
29408
|
}
|
|
29404
29409
|
var ProcessIncidentUseCase = class {
|
|
@@ -29473,14 +29478,17 @@ var ProcessIncidentUseCase = class {
|
|
|
29473
29478
|
});
|
|
29474
29479
|
let analysis = null;
|
|
29475
29480
|
let llmError = null;
|
|
29481
|
+
let llmDegradedReason = null;
|
|
29476
29482
|
try {
|
|
29477
29483
|
const llmResult = await llm.analyze(cluster, allSpans);
|
|
29478
29484
|
analysis = llmResult.analysis;
|
|
29485
|
+
llmDegradedReason = llmResult.degradedReason ?? null;
|
|
29479
29486
|
builder.set("llm", {
|
|
29480
29487
|
provider: llmResult.provider,
|
|
29481
29488
|
model: llmResult.model,
|
|
29482
29489
|
latencyMs: llmResult.latencyMs,
|
|
29483
|
-
urgency: llmResult.analysis.urgency_level,
|
|
29490
|
+
...llmResult.analysis && { urgency: llmResult.analysis.urgency_level },
|
|
29491
|
+
...llmResult.degradedReason && { degradedReason: llmResult.degradedReason },
|
|
29484
29492
|
tokens: llmResult.promptTokens + llmResult.completionTokens
|
|
29485
29493
|
});
|
|
29486
29494
|
} catch (err) {
|
|
@@ -29529,7 +29537,8 @@ var ProcessIncidentUseCase = class {
|
|
|
29529
29537
|
}
|
|
29530
29538
|
this.emit(builder, resolveOutcome({
|
|
29531
29539
|
llmError,
|
|
29532
|
-
notifyError: null
|
|
29540
|
+
notifyError: null,
|
|
29541
|
+
llmDegradedReason
|
|
29533
29542
|
}), clusterStartMs);
|
|
29534
29543
|
}
|
|
29535
29544
|
}
|
|
@@ -30893,21 +30902,25 @@ function buildUserPrompt(cluster, traces) {
|
|
|
30893
30902
|
}
|
|
30894
30903
|
/**
|
|
30895
30904
|
* Extracts LLMAnalysis from raw LLM response text.
|
|
30896
|
-
*
|
|
30897
|
-
*
|
|
30905
|
+
* Two-stage parsing: JSON → regex fallback. Never fabricates a diagnosis —
|
|
30906
|
+
* returns null when neither stage produces a usable analysis.
|
|
30898
30907
|
*/
|
|
30899
30908
|
function parseAnalysis(raw, correlationId) {
|
|
30900
30909
|
const startIdx = raw.indexOf("{");
|
|
30901
30910
|
const endIdx = raw.lastIndexOf("}");
|
|
30911
|
+
let stage1Succeeded = false;
|
|
30902
30912
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
30903
30913
|
try {
|
|
30904
|
-
|
|
30905
|
-
|
|
30906
|
-
|
|
30907
|
-
|
|
30908
|
-
|
|
30909
|
-
|
|
30910
|
-
|
|
30914
|
+
const analysis = LLMAnalysisSchema.parse(JSON.parse(raw.slice(startIdx, endIdx + 1)));
|
|
30915
|
+
stage1Succeeded = true;
|
|
30916
|
+
return analysis;
|
|
30917
|
+
} catch {}
|
|
30918
|
+
}
|
|
30919
|
+
if (!stage1Succeeded) {
|
|
30920
|
+
logger$6.warn({
|
|
30921
|
+
rawResponse: raw.slice(0, 500),
|
|
30922
|
+
correlationId
|
|
30923
|
+
}, "llm:parse:failed");
|
|
30911
30924
|
}
|
|
30912
30925
|
const probableCauseMatch = RE_PROBABLE_CAUSE.exec(raw);
|
|
30913
30926
|
const urgencyMatch = RE_URGENCY_LEVEL.exec(raw);
|
|
@@ -30926,20 +30939,34 @@ function parseAnalysis(raw, correlationId) {
|
|
|
30926
30939
|
urgency_level: urgency,
|
|
30927
30940
|
requires_rollback: rollbackMatch?.[1] === "true"
|
|
30928
30941
|
};
|
|
30929
|
-
|
|
30930
|
-
|
|
30931
|
-
|
|
30932
|
-
|
|
30933
|
-
|
|
30934
|
-
|
|
30935
|
-
|
|
30936
|
-
|
|
30937
|
-
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
|
|
30941
|
-
|
|
30942
|
-
|
|
30942
|
+
const parsed = LLMAnalysisSchema.parse(analysis);
|
|
30943
|
+
logger$6.warn({
|
|
30944
|
+
matchedFields: ["probable_cause", "urgency_level"],
|
|
30945
|
+
correlationId
|
|
30946
|
+
}, "llm:parse:partial");
|
|
30947
|
+
return parsed;
|
|
30948
|
+
}
|
|
30949
|
+
logger$6.warn({ correlationId }, "llm:parse:unusable");
|
|
30950
|
+
return null;
|
|
30951
|
+
}
|
|
30952
|
+
/**
|
|
30953
|
+
* Shared entry point for turning provider text into a diagnosis (or a
|
|
30954
|
+
* degradedReason explaining why not). Detects empty responses once, here,
|
|
30955
|
+
* before delegating to parseAnalysis.
|
|
30956
|
+
*/
|
|
30957
|
+
function parseLlmText(raw, correlationId) {
|
|
30958
|
+
if (raw.trim() === "") {
|
|
30959
|
+
logger$6.warn({ correlationId }, "llm:parse:empty");
|
|
30960
|
+
return {
|
|
30961
|
+
analysis: null,
|
|
30962
|
+
degradedReason: "empty_response"
|
|
30963
|
+
};
|
|
30964
|
+
}
|
|
30965
|
+
const analysis = parseAnalysis(raw, correlationId);
|
|
30966
|
+
return analysis ? { analysis } : {
|
|
30967
|
+
analysis: null,
|
|
30968
|
+
degradedReason: "unparseable_response"
|
|
30969
|
+
};
|
|
30943
30970
|
}
|
|
30944
30971
|
/**
|
|
30945
30972
|
* Gemini LLM provider using Google Generative AI SDK.
|
|
@@ -30981,7 +31008,7 @@ var GeminiProvider = class {
|
|
|
30981
31008
|
const result = await gemini.generateContent(buildUserPrompt(cluster, traces));
|
|
30982
31009
|
const usage = result.response.usageMetadata;
|
|
30983
31010
|
return {
|
|
30984
|
-
|
|
31011
|
+
...parseLlmText(result.response.text()),
|
|
30985
31012
|
promptTokens: usage?.promptTokenCount ?? 0,
|
|
30986
31013
|
completionTokens: usage?.candidatesTokenCount ?? 0
|
|
30987
31014
|
};
|
|
@@ -31013,7 +31040,7 @@ var ClaudeProvider = class {
|
|
|
31013
31040
|
});
|
|
31014
31041
|
const text = message.content.find((b) => b.type === "text")?.text ?? "";
|
|
31015
31042
|
return {
|
|
31016
|
-
|
|
31043
|
+
...parseLlmText(text),
|
|
31017
31044
|
provider: "claude",
|
|
31018
31045
|
model: this.model,
|
|
31019
31046
|
latencyMs: Date.now() - startMs,
|
|
@@ -31133,7 +31160,7 @@ var OpenRouterProvider = class {
|
|
|
31133
31160
|
}, "llm:validation:failed");
|
|
31134
31161
|
}
|
|
31135
31162
|
const text = parsed.success ? parsed.data.choices?.[0]?.message?.content ?? "" : "";
|
|
31136
|
-
const
|
|
31163
|
+
const parsedAnalysis = parseLlmText(text, correlationId);
|
|
31137
31164
|
const usage = parsed.success ? parsed.data.usage : undefined;
|
|
31138
31165
|
if (usage) {
|
|
31139
31166
|
const { prompt_tokens, completion_tokens, total_tokens } = usage;
|
|
@@ -31151,7 +31178,7 @@ var OpenRouterProvider = class {
|
|
|
31151
31178
|
llmInferenceTotal.inc({ status: "success" });
|
|
31152
31179
|
llmInferenceDuration.observe({ model: this.model }, latencyMs / 1e3);
|
|
31153
31180
|
return {
|
|
31154
|
-
|
|
31181
|
+
...parsedAnalysis,
|
|
31155
31182
|
provider: this.providerName,
|
|
31156
31183
|
model: this.model,
|
|
31157
31184
|
latencyMs,
|
|
@@ -31203,7 +31230,7 @@ var OpenRouterProvider = class {
|
|
|
31203
31230
|
const text = parsed.success ? parsed.data.choices?.[0]?.message?.content ?? "" : "";
|
|
31204
31231
|
const usage = parsed.success ? parsed.data.usage : undefined;
|
|
31205
31232
|
return {
|
|
31206
|
-
|
|
31233
|
+
...parseLlmText(text, correlationId),
|
|
31207
31234
|
provider: this.providerName,
|
|
31208
31235
|
model: toModel,
|
|
31209
31236
|
latencyMs: Date.now() - startMs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junando/worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "AWS Lambda SQS worker — processes alert events and dispatches notifications for Junando",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@aws-sdk/client-ssm": "^3.1127.0",
|
|
23
23
|
"ioredis": "^6.0.0",
|
|
24
24
|
"zod": "^4.5.4",
|
|
25
|
-
"@junando/core": "0.
|
|
25
|
+
"@junando/core": "0.13.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/aws-lambda": "^8.10.163",
|