@hiper2d/ai-agents 0.5.1 → 0.5.2
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.d.mts +21 -7
- package/dist/index.d.ts +21 -7
- package/dist/index.js +47 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +47 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -272,15 +272,29 @@ declare class ModelInvalidResponseError extends ModelError {
|
|
|
272
272
|
constructor(modelType: string, detail: string, truncated?: boolean);
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
|
-
* The model declined to answer
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
275
|
+
* The model declined to answer. Not retryable as-is — the same prompt will refuse again —
|
|
276
|
+
* the caller has to change the prompt or the model. `reason` is the provider's own label
|
|
277
|
+
* for the refusal, when it gives one:
|
|
278
|
+
*
|
|
279
|
+
* Anthropic `stop_reason: "refusal"` with no content blocks when its safety layer rejects
|
|
280
|
+
* the request as a whole. Observed 2026-08-30 on Claude Fable 5: a persona
|
|
281
|
+
* system prompt plus a narrated multi-turn history that ends by asking the
|
|
282
|
+
* character what it does refuses, while either half alone answers; Sonnet 5
|
|
283
|
+
* and Opus 4.8 answer the same requests. `reason` = "refusal".
|
|
284
|
+
* Google a 200 with no candidates and `promptFeedback.blockReason` set (the prompt
|
|
285
|
+
* itself was rejected: PROHIBITED_CONTENT, SAFETY, BLOCKLIST, JAILBREAK,
|
|
286
|
+
* MODEL_ARMOR, OTHER), or a candidate whose `finishReason` is a content
|
|
287
|
+
* block (SAFETY, PROHIBITED_CONTENT, BLOCKLIST, RECITATION, SPII,
|
|
288
|
+
* IMAGE_SAFETY, ...). Observed 2026-09-13 in production: an explicit
|
|
289
|
+
* sexual roleplay game was refused by Gemini 3.8 Flash and 3.1 Pro with
|
|
290
|
+
* blockReason PROHIBITED_CONTENT in ~200ms and zero output tokens, while
|
|
291
|
+
* Mistral, MiniMax and Muse answered the same prompt. PROHIBITED_CONTENT is
|
|
292
|
+
* Google's non-configurable filter — no safetySettings change lifts it.
|
|
293
|
+
* `reason` = the blockReason or finishReason string.
|
|
281
294
|
*/
|
|
282
295
|
declare class ModelRefusalError extends ModelError {
|
|
283
|
-
|
|
296
|
+
reason?: string;
|
|
297
|
+
constructor(modelType: string, message?: string, reason?: string);
|
|
284
298
|
}
|
|
285
299
|
|
|
286
300
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -272,15 +272,29 @@ declare class ModelInvalidResponseError extends ModelError {
|
|
|
272
272
|
constructor(modelType: string, detail: string, truncated?: boolean);
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
|
-
* The model declined to answer
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
275
|
+
* The model declined to answer. Not retryable as-is — the same prompt will refuse again —
|
|
276
|
+
* the caller has to change the prompt or the model. `reason` is the provider's own label
|
|
277
|
+
* for the refusal, when it gives one:
|
|
278
|
+
*
|
|
279
|
+
* Anthropic `stop_reason: "refusal"` with no content blocks when its safety layer rejects
|
|
280
|
+
* the request as a whole. Observed 2026-08-30 on Claude Fable 5: a persona
|
|
281
|
+
* system prompt plus a narrated multi-turn history that ends by asking the
|
|
282
|
+
* character what it does refuses, while either half alone answers; Sonnet 5
|
|
283
|
+
* and Opus 4.8 answer the same requests. `reason` = "refusal".
|
|
284
|
+
* Google a 200 with no candidates and `promptFeedback.blockReason` set (the prompt
|
|
285
|
+
* itself was rejected: PROHIBITED_CONTENT, SAFETY, BLOCKLIST, JAILBREAK,
|
|
286
|
+
* MODEL_ARMOR, OTHER), or a candidate whose `finishReason` is a content
|
|
287
|
+
* block (SAFETY, PROHIBITED_CONTENT, BLOCKLIST, RECITATION, SPII,
|
|
288
|
+
* IMAGE_SAFETY, ...). Observed 2026-09-13 in production: an explicit
|
|
289
|
+
* sexual roleplay game was refused by Gemini 3.8 Flash and 3.1 Pro with
|
|
290
|
+
* blockReason PROHIBITED_CONTENT in ~200ms and zero output tokens, while
|
|
291
|
+
* Mistral, MiniMax and Muse answered the same prompt. PROHIBITED_CONTENT is
|
|
292
|
+
* Google's non-configurable filter — no safetySettings change lifts it.
|
|
293
|
+
* `reason` = the blockReason or finishReason string.
|
|
281
294
|
*/
|
|
282
295
|
declare class ModelRefusalError extends ModelError {
|
|
283
|
-
|
|
296
|
+
reason?: string;
|
|
297
|
+
constructor(modelType: string, message?: string, reason?: string);
|
|
284
298
|
}
|
|
285
299
|
|
|
286
300
|
/**
|
package/dist/index.js
CHANGED
|
@@ -883,9 +883,11 @@ var ModelInvalidResponseError = class extends ModelError {
|
|
|
883
883
|
}
|
|
884
884
|
};
|
|
885
885
|
var ModelRefusalError = class extends ModelError {
|
|
886
|
-
|
|
886
|
+
reason;
|
|
887
|
+
constructor(modelType, message = `${modelType} refused to answer (stop_reason: refusal)`, reason) {
|
|
887
888
|
super(message, modelType);
|
|
888
889
|
this.name = "ModelRefusalError";
|
|
890
|
+
this.reason = reason;
|
|
889
891
|
}
|
|
890
892
|
};
|
|
891
893
|
|
|
@@ -2823,7 +2825,7 @@ ${schemaDescription}`;
|
|
|
2823
2825
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
2824
2826
|
}
|
|
2825
2827
|
if (response.stop_reason === "refusal") {
|
|
2826
|
-
throw new ModelRefusalError(this.model);
|
|
2828
|
+
throw new ModelRefusalError(this.model, void 0, "refusal");
|
|
2827
2829
|
}
|
|
2828
2830
|
if (!("content" in response) || !Array.isArray(response.content) || response.content.length === 0) {
|
|
2829
2831
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -2917,7 +2919,7 @@ ${schemaDescription}`;
|
|
|
2917
2919
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
2918
2920
|
}
|
|
2919
2921
|
if (response.stop_reason === "refusal") {
|
|
2920
|
-
throw new ModelRefusalError(this.model);
|
|
2922
|
+
throw new ModelRefusalError(this.model, void 0, "refusal");
|
|
2921
2923
|
}
|
|
2922
2924
|
if (!("content" in response) || !Array.isArray(response.content) || response.content.length === 0) {
|
|
2923
2925
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -2973,6 +2975,45 @@ ${schemaDescription}`;
|
|
|
2973
2975
|
|
|
2974
2976
|
// src/agents/google-agent.ts
|
|
2975
2977
|
var import_genai3 = require("@google/genai");
|
|
2978
|
+
var GEMINI_REFUSAL_FINISH_REASONS = /* @__PURE__ */ new Set([
|
|
2979
|
+
"SAFETY",
|
|
2980
|
+
"RECITATION",
|
|
2981
|
+
"BLOCKLIST",
|
|
2982
|
+
"PROHIBITED_CONTENT",
|
|
2983
|
+
"SPII",
|
|
2984
|
+
"IMAGE_SAFETY",
|
|
2985
|
+
"IMAGE_PROHIBITED_CONTENT",
|
|
2986
|
+
"IMAGE_RECITATION"
|
|
2987
|
+
]);
|
|
2988
|
+
function throwForEmptyGeminiResponse(model, response, fallback) {
|
|
2989
|
+
const blockReason = response?.promptFeedback?.blockReason;
|
|
2990
|
+
const candidate = response?.candidates?.[0];
|
|
2991
|
+
const finishReason = candidate?.finishReason;
|
|
2992
|
+
const finishMessage = candidate?.finishMessage;
|
|
2993
|
+
const ratings = (candidate?.safetyRatings ?? response?.promptFeedback?.safetyRatings ?? []).filter((r) => r?.blocked || r?.probability && r.probability !== "NEGLIGIBLE");
|
|
2994
|
+
const ratingText = ratings.length ? `; safetyRatings: ${ratings.map((r) => `${r.category}=${r.probability ?? (r.blocked ? "blocked" : "?")}`).join(", ")}` : "";
|
|
2995
|
+
if (blockReason) {
|
|
2996
|
+
throw new ModelRefusalError(
|
|
2997
|
+
model,
|
|
2998
|
+
`${model} refused the prompt (blockReason: ${blockReason}${finishMessage ? `, ${finishMessage}` : ""}${ratingText})`,
|
|
2999
|
+
blockReason
|
|
3000
|
+
);
|
|
3001
|
+
}
|
|
3002
|
+
if (finishReason && GEMINI_REFUSAL_FINISH_REASONS.has(finishReason)) {
|
|
3003
|
+
throw new ModelRefusalError(
|
|
3004
|
+
model,
|
|
3005
|
+
`${model} refused to answer (finishReason: ${finishReason}${finishMessage ? `, ${finishMessage}` : ""}${ratingText})`,
|
|
3006
|
+
finishReason
|
|
3007
|
+
);
|
|
3008
|
+
}
|
|
3009
|
+
if (finishReason === "MAX_TOKENS") {
|
|
3010
|
+
throw new ModelInvalidResponseError(model, "empty response, finishReason MAX_TOKENS (thinking consumed the whole output budget)", true);
|
|
3011
|
+
}
|
|
3012
|
+
const bits = [`finishReason=${finishReason ?? "none"}`];
|
|
3013
|
+
if (!response?.candidates?.length) bits.push("no candidates");
|
|
3014
|
+
if (finishMessage) bits.push(`finishMessage="${finishMessage}"`);
|
|
3015
|
+
throw new Error(`${fallback} (${bits.join(", ")})`);
|
|
3016
|
+
}
|
|
2976
3017
|
var GoogleAgent = class extends AbstractAgent {
|
|
2977
3018
|
client;
|
|
2978
3019
|
defaultConfig = {
|
|
@@ -2984,7 +3025,7 @@ var GoogleAgent = class extends AbstractAgent {
|
|
|
2984
3025
|
};
|
|
2985
3026
|
// Error message templates
|
|
2986
3027
|
errorMessages = {
|
|
2987
|
-
emptyResponse: "Empty response from Google API
|
|
3028
|
+
emptyResponse: "Empty response from Google API",
|
|
2988
3029
|
invalidFormat: "Invalid response format from Google API",
|
|
2989
3030
|
apiError: (error) => `Failed to get response from Google API: ${error instanceof Error ? error.message : String(error)}`,
|
|
2990
3031
|
unsupportedRole: (role) => `Unsupported role type: ${role}`
|
|
@@ -3156,7 +3197,7 @@ var GoogleAgent = class extends AbstractAgent {
|
|
|
3156
3197
|
}
|
|
3157
3198
|
this.logger(`Zod schema response received - hasText: ${!!response.text}, textLength: ${response.text ? response.text.length : 0}`);
|
|
3158
3199
|
if (!response.text) {
|
|
3159
|
-
|
|
3200
|
+
throwForEmptyGeminiResponse(this.model, response, this.errorMessages.emptyResponse);
|
|
3160
3201
|
}
|
|
3161
3202
|
const parsedData = parseAndValidateLlmJson(response.text, zodSchema, (m) => this.logger(m));
|
|
3162
3203
|
if (parsedData) {
|
|
@@ -3244,7 +3285,7 @@ var GoogleAgent = class extends AbstractAgent {
|
|
|
3244
3285
|
}
|
|
3245
3286
|
this.logger(`Plain text response received - hasText: ${!!response.text}, textLength: ${response.text ? response.text.length : 0}`);
|
|
3246
3287
|
if (!response.text) {
|
|
3247
|
-
|
|
3288
|
+
throwForEmptyGeminiResponse(this.model, response, this.errorMessages.emptyResponse);
|
|
3248
3289
|
}
|
|
3249
3290
|
this.logReply(response.text, thinkingContent || void 0, tokenUsage);
|
|
3250
3291
|
return [response.text, thinkingContent, tokenUsage, googleThoughtSignature || void 0];
|