langchain
Version:
Typescript bindings for langchain
71 lines (70 loc) • 2.45 kB
JavaScript
import { MiddlewareError, StructuredOutputParsingError } from "../errors.js";
import { createMiddleware } from "../middleware.js";
import { AIMessage } from "@langchain/core/messages";
import { ModelRefusalError } from "@langchain/core/errors";
// src/agents/middleware/modelRefusal.ts
function throwIfRefused(message) {
if (!message) return;
for (const block of message.contentBlocks) if (block.type === "refusal") throw ModelRefusalError.fromRefusal(block, message);
}
/**
* The `StructuredOutputParsingError` behind `error`, if any.
*
* Middleware listed after this one wraps errors in `MiddlewareError`, so the
* parsing error can sit further down the `cause` chain.
*/
function parsingError(error) {
let current = error;
while (MiddlewareError.isInstance(current)) current = current.cause;
return current instanceof StructuredOutputParsingError ? current : void 0;
}
/**
* Middleware that throws {@link ModelRefusalError} when the model refuses.
*
* Providers report a refusal, such as a safety-policy refusal, as a successful
* response, so without this middleware the agent run ends as if it succeeded.
* The middleware checks each model response for a `refusal` content block,
* including refusals that fail to parse under a structured `responseFormat`.
* It does not detect refusals the model writes as ordinary text.
*
* List it after {@link modelFallbackMiddleware} so the fallback catches the
* error and retries the request on the next model. `modelRetryMiddleware` does
* not retry refusals.
*
* @example
* ```ts
* import {
* createAgent,
* modelFallbackMiddleware,
* modelRefusalMiddleware,
* } from "langchain";
*
* const agent = createAgent({
* model: "anthropic:claude-sonnet-5-5",
* tools: [],
* middleware: [
* modelFallbackMiddleware("openai:gpt-6.1-sol"),
* modelRefusalMiddleware(),
* ],
* });
* ```
*/
function modelRefusalMiddleware() {
return createMiddleware({
name: "modelRefusalMiddleware",
wrapModelCall: async (request, handler) => {
let response;
try {
response = await handler(request);
} catch (error) {
throwIfRefused(parsingError(error)?.aiMessage);
throw error;
}
const messages = AIMessage.isInstance(response) ? [response] : response.messages ?? [];
for (const message of messages) if (AIMessage.isInstance(message)) throwIfRefused(message);
return response;
}
});
}
export { modelRefusalMiddleware };
//# sourceMappingURL=modelRefusal.js.map