@sentry/junior 0.28.0 → 0.30.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/app.js +707 -479
- package/dist/{chunk-375D5V4U.js → chunk-LEYD42MR.js} +256 -4
- package/dist/cli/snapshot-warmup.js +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
extractGenAiUsageAttributes,
|
|
2
3
|
getPluginRuntimeDependencies,
|
|
3
4
|
getPluginRuntimePostinstall,
|
|
5
|
+
logException,
|
|
6
|
+
logWarn,
|
|
7
|
+
serializeGenAiAttribute,
|
|
8
|
+
setSpanAttributes,
|
|
4
9
|
withSpan
|
|
5
10
|
} from "./chunk-RZJDO55D.js";
|
|
6
11
|
|
|
@@ -8,6 +13,9 @@ import {
|
|
|
8
13
|
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
9
14
|
import { createRedisState } from "@chat-adapter/state-redis";
|
|
10
15
|
|
|
16
|
+
// src/chat/config.ts
|
|
17
|
+
import { getModel } from "@mariozechner/pi-ai";
|
|
18
|
+
|
|
11
19
|
// src/chat/optional-string.ts
|
|
12
20
|
function toOptionalTrimmed(value) {
|
|
13
21
|
if (!value) {
|
|
@@ -17,6 +25,233 @@ function toOptionalTrimmed(value) {
|
|
|
17
25
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
18
26
|
}
|
|
19
27
|
|
|
28
|
+
// src/chat/pi/client.ts
|
|
29
|
+
import {
|
|
30
|
+
completeSimple,
|
|
31
|
+
getEnvApiKey,
|
|
32
|
+
getModels,
|
|
33
|
+
registerApiProvider
|
|
34
|
+
} from "@mariozechner/pi-ai";
|
|
35
|
+
import {
|
|
36
|
+
streamAnthropic,
|
|
37
|
+
streamSimpleAnthropic
|
|
38
|
+
} from "@mariozechner/pi-ai/anthropic";
|
|
39
|
+
registerApiProvider({
|
|
40
|
+
api: "anthropic-messages",
|
|
41
|
+
stream: streamAnthropic,
|
|
42
|
+
streamSimple: streamSimpleAnthropic
|
|
43
|
+
});
|
|
44
|
+
var GATEWAY_PROVIDER = "vercel-ai-gateway";
|
|
45
|
+
var GEN_AI_PROVIDER_NAME = GATEWAY_PROVIDER;
|
|
46
|
+
var GEN_AI_OPERATION_CHAT = "chat";
|
|
47
|
+
var MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN)";
|
|
48
|
+
function getGatewayApiKey() {
|
|
49
|
+
return toOptionalTrimmed(getEnvApiKey("vercel-ai-gateway")) ?? toOptionalTrimmed(process.env.VERCEL_OIDC_TOKEN);
|
|
50
|
+
}
|
|
51
|
+
function getPiGatewayApiKeyOverride() {
|
|
52
|
+
return toOptionalTrimmed(process.env.VERCEL_OIDC_TOKEN);
|
|
53
|
+
}
|
|
54
|
+
function extractText(message) {
|
|
55
|
+
return (message.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join("").trim();
|
|
56
|
+
}
|
|
57
|
+
function parseJsonCandidate(text) {
|
|
58
|
+
const trimmed = text.trim();
|
|
59
|
+
if (!trimmed) return void 0;
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(trimmed);
|
|
62
|
+
} catch {
|
|
63
|
+
const fencedBlocks = [
|
|
64
|
+
...trimmed.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)
|
|
65
|
+
];
|
|
66
|
+
for (const block of fencedBlocks) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(block[1]);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const openBraceIndex = trimmed.indexOf("{");
|
|
73
|
+
if (openBraceIndex >= 0) {
|
|
74
|
+
let depth = 0;
|
|
75
|
+
let inString = false;
|
|
76
|
+
let escaped = false;
|
|
77
|
+
for (let index = openBraceIndex; index < trimmed.length; index += 1) {
|
|
78
|
+
const char = trimmed[index];
|
|
79
|
+
if (inString) {
|
|
80
|
+
if (escaped) {
|
|
81
|
+
escaped = false;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (char === "\\") {
|
|
85
|
+
escaped = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (char === '"') {
|
|
89
|
+
inString = false;
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (char === '"') {
|
|
94
|
+
inString = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (char === "{") {
|
|
98
|
+
depth += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (char === "}") {
|
|
102
|
+
depth -= 1;
|
|
103
|
+
if (depth === 0) {
|
|
104
|
+
const slice = trimmed.slice(openBraceIndex, index + 1);
|
|
105
|
+
try {
|
|
106
|
+
return JSON.parse(slice);
|
|
107
|
+
} catch {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function resolveGatewayModel(modelId) {
|
|
118
|
+
const matched = getModels(GATEWAY_PROVIDER).find(
|
|
119
|
+
(model) => model.id === modelId
|
|
120
|
+
);
|
|
121
|
+
if (!matched) {
|
|
122
|
+
throw new Error(`Unknown AI Gateway model id: ${modelId}`);
|
|
123
|
+
}
|
|
124
|
+
return matched;
|
|
125
|
+
}
|
|
126
|
+
async function completeText(params) {
|
|
127
|
+
const model = resolveGatewayModel(params.modelId);
|
|
128
|
+
const apiKey = getPiGatewayApiKeyOverride();
|
|
129
|
+
const requestMessagesAttribute = serializeGenAiAttribute(params.messages);
|
|
130
|
+
const systemInstructionsAttribute = params.system ? serializeGenAiAttribute([{ type: "text", content: params.system }]) : void 0;
|
|
131
|
+
const startAttributes = {
|
|
132
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
133
|
+
"gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
|
|
134
|
+
"gen_ai.request.model": params.modelId,
|
|
135
|
+
...systemInstructionsAttribute ? { "gen_ai.system_instructions": systemInstructionsAttribute } : {},
|
|
136
|
+
...requestMessagesAttribute ? { "gen_ai.input.messages": requestMessagesAttribute } : {},
|
|
137
|
+
"app.ai.auth_mode": apiKey ? "oidc" : "api_key",
|
|
138
|
+
...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
|
|
139
|
+
};
|
|
140
|
+
setSpanAttributes(startAttributes);
|
|
141
|
+
const message = await completeSimple(
|
|
142
|
+
model,
|
|
143
|
+
{
|
|
144
|
+
systemPrompt: params.system,
|
|
145
|
+
messages: params.messages
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
...apiKey ? { apiKey } : {},
|
|
149
|
+
temperature: params.temperature,
|
|
150
|
+
maxTokens: params.maxTokens,
|
|
151
|
+
reasoning: params.thinkingLevel,
|
|
152
|
+
signal: params.signal,
|
|
153
|
+
metadata: params.metadata
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
const outputText = extractText(message);
|
|
157
|
+
const outputMessagesAttribute = serializeGenAiAttribute([
|
|
158
|
+
{
|
|
159
|
+
role: "assistant",
|
|
160
|
+
content: outputText ? [{ type: "text", text: outputText }] : []
|
|
161
|
+
}
|
|
162
|
+
]);
|
|
163
|
+
const usageAttributes = extractGenAiUsageAttributes(message);
|
|
164
|
+
const endAttributes = {
|
|
165
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
166
|
+
"gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
|
|
167
|
+
"gen_ai.request.model": params.modelId,
|
|
168
|
+
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
169
|
+
...usageAttributes,
|
|
170
|
+
...message.stopReason ? { "gen_ai.response.finish_reasons": [message.stopReason] } : {},
|
|
171
|
+
...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
|
|
172
|
+
};
|
|
173
|
+
setSpanAttributes(endAttributes);
|
|
174
|
+
if (message.stopReason === "error") {
|
|
175
|
+
const providerMessage = message.errorMessage?.trim() || "Unknown provider error";
|
|
176
|
+
logWarn(
|
|
177
|
+
"ai_completion_provider_error",
|
|
178
|
+
{},
|
|
179
|
+
{
|
|
180
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
181
|
+
"gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
|
|
182
|
+
"gen_ai.request.model": params.modelId,
|
|
183
|
+
"error.message": providerMessage
|
|
184
|
+
},
|
|
185
|
+
"AI completion returned provider error"
|
|
186
|
+
);
|
|
187
|
+
throw new Error(`AI provider error: ${providerMessage}`);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
message,
|
|
191
|
+
text: outputText
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async function completeObject(params) {
|
|
195
|
+
const startedAt = Date.now();
|
|
196
|
+
let text = "";
|
|
197
|
+
try {
|
|
198
|
+
({ text } = await completeText({
|
|
199
|
+
modelId: params.modelId,
|
|
200
|
+
system: params.system,
|
|
201
|
+
thinkingLevel: params.thinkingLevel,
|
|
202
|
+
temperature: params.temperature,
|
|
203
|
+
maxTokens: params.maxTokens,
|
|
204
|
+
signal: params.signal,
|
|
205
|
+
metadata: params.metadata,
|
|
206
|
+
messages: [
|
|
207
|
+
{
|
|
208
|
+
role: "user",
|
|
209
|
+
content: params.prompt,
|
|
210
|
+
timestamp: Date.now()
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}));
|
|
214
|
+
} catch (error) {
|
|
215
|
+
logException(
|
|
216
|
+
error,
|
|
217
|
+
"ai_completion_failed",
|
|
218
|
+
{},
|
|
219
|
+
{
|
|
220
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
221
|
+
"gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
|
|
222
|
+
"gen_ai.request.model": params.modelId,
|
|
223
|
+
"app.ai.duration_ms": Date.now() - startedAt
|
|
224
|
+
},
|
|
225
|
+
"AI object completion failed"
|
|
226
|
+
);
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
const candidate = parseJsonCandidate(text);
|
|
230
|
+
const parsed = params.schema.safeParse(candidate);
|
|
231
|
+
if (!parsed.success) {
|
|
232
|
+
const preview = text.length > 400 ? `${text.slice(0, 400)}...` : text;
|
|
233
|
+
logWarn(
|
|
234
|
+
"ai_completion_schema_parse_failed",
|
|
235
|
+
{},
|
|
236
|
+
{
|
|
237
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
238
|
+
"gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
|
|
239
|
+
"gen_ai.request.model": params.modelId,
|
|
240
|
+
"app.ai.duration_ms": Date.now() - startedAt,
|
|
241
|
+
"app.ai.response_preview": preview
|
|
242
|
+
},
|
|
243
|
+
"AI object completion schema parse failed"
|
|
244
|
+
);
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Model did not return valid JSON for schema: ${parsed.error.message}. Raw response: ${preview}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
object: parsed.data,
|
|
251
|
+
text
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
20
255
|
// src/chat/config.ts
|
|
21
256
|
var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
|
|
22
257
|
var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
|
|
@@ -79,15 +314,26 @@ function parseLoadingMessages(rawValue) {
|
|
|
79
314
|
return value.trim();
|
|
80
315
|
});
|
|
81
316
|
}
|
|
317
|
+
var DEFAULT_MODEL_ID = getModel("vercel-ai-gateway", "openai/gpt-5.4").id;
|
|
318
|
+
var DEFAULT_FAST_MODEL_ID = getModel(
|
|
319
|
+
"vercel-ai-gateway",
|
|
320
|
+
"openai/gpt-5.4-mini"
|
|
321
|
+
).id;
|
|
322
|
+
function validateGatewayModelId(raw) {
|
|
323
|
+
const trimmed = toOptionalTrimmed(raw);
|
|
324
|
+
if (trimmed === void 0) return void 0;
|
|
325
|
+
resolveGatewayModel(trimmed);
|
|
326
|
+
return trimmed;
|
|
327
|
+
}
|
|
82
328
|
function readBotConfig(env) {
|
|
83
329
|
const functionMaxDurationSeconds = resolveFunctionMaxDurationSeconds(env);
|
|
84
330
|
const maxTurnTimeoutMs = resolveMaxTurnTimeoutMs(functionMaxDurationSeconds);
|
|
85
331
|
return {
|
|
86
332
|
userName: env.JUNIOR_BOT_NAME ?? "junior",
|
|
87
|
-
modelId: env.AI_MODEL ??
|
|
88
|
-
fastModelId: env.AI_FAST_MODEL ?? env.AI_MODEL ??
|
|
333
|
+
modelId: validateGatewayModelId(env.AI_MODEL) ?? DEFAULT_MODEL_ID,
|
|
334
|
+
fastModelId: validateGatewayModelId(env.AI_FAST_MODEL ?? env.AI_MODEL) ?? DEFAULT_FAST_MODEL_ID,
|
|
89
335
|
loadingMessages: parseLoadingMessages(env.JUNIOR_LOADING_MESSAGES),
|
|
90
|
-
visionModelId:
|
|
336
|
+
visionModelId: validateGatewayModelId(env.AI_VISION_MODEL),
|
|
91
337
|
turnTimeoutMs: parseAgentTurnTimeoutMs(
|
|
92
338
|
env.AGENT_TURN_TIMEOUT_MS,
|
|
93
339
|
maxTurnTimeoutMs
|
|
@@ -842,7 +1088,13 @@ function isSnapshotMissingError(error) {
|
|
|
842
1088
|
}
|
|
843
1089
|
|
|
844
1090
|
export {
|
|
845
|
-
|
|
1091
|
+
GEN_AI_PROVIDER_NAME,
|
|
1092
|
+
MISSING_GATEWAY_CREDENTIALS_ERROR,
|
|
1093
|
+
getGatewayApiKey,
|
|
1094
|
+
getPiGatewayApiKeyOverride,
|
|
1095
|
+
resolveGatewayModel,
|
|
1096
|
+
completeText,
|
|
1097
|
+
completeObject,
|
|
846
1098
|
botConfig,
|
|
847
1099
|
getSlackBotToken,
|
|
848
1100
|
getSlackSigningSecret,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"@chat-adapter/state-memory": "4.26.0",
|
|
26
26
|
"@chat-adapter/state-redis": "4.26.0",
|
|
27
27
|
"@logtape/logtape": "^2.0.5",
|
|
28
|
-
"@mariozechner/pi-agent-core": "0.
|
|
29
|
-
"@mariozechner/pi-ai": "0.
|
|
28
|
+
"@mariozechner/pi-agent-core": "0.68.1",
|
|
29
|
+
"@mariozechner/pi-ai": "0.68.1",
|
|
30
30
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
31
31
|
"@sinclair/typebox": "^0.34.49",
|
|
32
32
|
"@slack/web-api": "^7.15.1",
|