Studio: add Anthropic and OpenAI prompt guards for disabled tools (#5674)
* Add Anthropic prompt guards for disabled tools * fix: merge Anthropic tool guard into structured system prompts * fix: scope Anthropic disabled-tool guard wording * chore: adjust claude guard prompt * chore: add openai to list of prompt guarded providers * Studio: include web_fetch in the per-turn disabled-tool guard Add webFetchEnabledForThisTurn alongside webSearchEnabledForThisTurn and codeExecEnabledForThisTurn. Use it in the enabled_tools payload so web_fetch follows the Search pill the same way web_search does, and mention "web fetch" in the disabled-tool guard prose on providers that ship the tool (Anthropic today; other providers stay inert via providerSupportsBuiltinWebFetch). --------- Co-authored-by: Roland Tannous <115670425+rolandtannous@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
228d1cd40c
commit
51736a7766
1 changed files with 94 additions and 37 deletions
|
|
@ -869,6 +869,38 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
throw new Error("Missing connection API key.");
|
||||
}
|
||||
|
||||
const webSearchEnabledForThisTurn =
|
||||
Boolean(
|
||||
externalProvider &&
|
||||
toolsEnabled &&
|
||||
providerSupportsBuiltinWebSearch(externalProvider.providerType),
|
||||
);
|
||||
const codeExecEnabledForThisTurn =
|
||||
Boolean(
|
||||
externalProvider &&
|
||||
externalSelection &&
|
||||
codeToolsEnabled &&
|
||||
providerSupportsBuiltinCodeExecution(
|
||||
externalProvider.providerType,
|
||||
externalSelection.modelId,
|
||||
externalProvider.baseUrl,
|
||||
),
|
||||
);
|
||||
// web_fetch shares the Search pill with web_search (no separate
|
||||
// UI toggle), so it follows toolsEnabled. Anthropic is the only
|
||||
// provider that ships it today; on others providerSupportsBuiltinWebFetch
|
||||
// returns false and this stays inert.
|
||||
const webFetchEnabledForThisTurn =
|
||||
Boolean(
|
||||
externalProvider &&
|
||||
toolsEnabled &&
|
||||
providerSupportsBuiltinWebFetch(externalProvider.providerType),
|
||||
);
|
||||
const providerShipsWebFetch = Boolean(
|
||||
externalProvider &&
|
||||
providerSupportsBuiltinWebFetch(externalProvider.providerType),
|
||||
);
|
||||
|
||||
const outboundMessages = messages
|
||||
.map(toOpenAIMessage)
|
||||
.filter((message): message is NonNullable<typeof message> =>
|
||||
|
|
@ -883,6 +915,62 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
content: safeSystemPrompt.trim(),
|
||||
});
|
||||
}
|
||||
let disabledToolGuard: string | null = null;
|
||||
const disabledToolGuardProviderType = externalProvider?.providerType;
|
||||
if (
|
||||
disabledToolGuardProviderType === "anthropic" ||
|
||||
disabledToolGuardProviderType === "openai"
|
||||
) {
|
||||
const webLabel = providerShipsWebFetch
|
||||
? "web search or web fetch"
|
||||
: "web search";
|
||||
if (!webSearchEnabledForThisTurn && !codeExecEnabledForThisTurn) {
|
||||
disabledToolGuard =
|
||||
`You do not have ${webLabel} or code execution tools in this conversation. ` +
|
||||
"Answer from your own knowledge. " +
|
||||
"If a request genuinely requires tool use, live data fetch or running code, " +
|
||||
"inform the user that you do not have access to these capabilities. " +
|
||||
"Do not return tool-call syntax inside your response.";
|
||||
} else if (!webSearchEnabledForThisTurn) {
|
||||
disabledToolGuard =
|
||||
`You do not have ${webLabel} tools in this conversation. ` +
|
||||
"You may still use code execution tools when they are available and useful. " +
|
||||
"If a request genuinely requires live data fetch or web search tool use, " +
|
||||
"inform the user that you do not have access to these capabilities. " +
|
||||
"Do not return tool-call syntax inside your response.";
|
||||
} else if (!codeExecEnabledForThisTurn) {
|
||||
disabledToolGuard =
|
||||
"You do not have code execution tools in this conversation. " +
|
||||
`You may still use ${webLabel} tools when they are available and useful. ` +
|
||||
"If a request genuinely requires running code or code execution tool use, " +
|
||||
"inform the user that you do not have access to these capabilities. " +
|
||||
"Do not return tool-call syntax inside your response.";
|
||||
}
|
||||
}
|
||||
if (disabledToolGuard) {
|
||||
const firstMessage = outboundMessages[0];
|
||||
if (firstMessage?.role === "system") {
|
||||
if (typeof firstMessage.content === "string") {
|
||||
outboundMessages[0] = {
|
||||
...firstMessage,
|
||||
content: `${firstMessage.content}\n\n${disabledToolGuard}`,
|
||||
};
|
||||
} else {
|
||||
outboundMessages[0] = {
|
||||
...firstMessage,
|
||||
content: [
|
||||
...firstMessage.content,
|
||||
{ type: "text", text: `\n\n${disabledToolGuard}` },
|
||||
],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
outboundMessages.unshift({
|
||||
role: "system",
|
||||
content: disabledToolGuard,
|
||||
});
|
||||
}
|
||||
}
|
||||
const imageBase64 = findLatestUserImageBase64(messages);
|
||||
const audioBase64 = findLatestUserAudioBase64(messages);
|
||||
|
||||
|
|
@ -1137,13 +1225,6 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
// (sent as `container` on /v1/messages).
|
||||
let openaiCodeExecContainerId: string | null = null;
|
||||
let anthropicCodeExecContainerId: string | null = null;
|
||||
const codeExecEnabledForThisTurn =
|
||||
codeToolsEnabled &&
|
||||
providerSupportsBuiltinCodeExecution(
|
||||
externalProvider.providerType,
|
||||
externalSelection.modelId,
|
||||
externalProvider.baseUrl,
|
||||
);
|
||||
if (codeExecEnabledForThisTurn && resolvedThreadId) {
|
||||
try {
|
||||
const thread = await getStoredChatThread(resolvedThreadId);
|
||||
|
|
@ -1313,25 +1394,13 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
// translates enabled_tools into each provider's tool
|
||||
// schema — for Anthropic that's the entries appended to
|
||||
// body["tools"] inside _stream_anthropic.
|
||||
...((toolsEnabled &&
|
||||
providerSupportsBuiltinWebSearch(
|
||||
externalProvider.providerType,
|
||||
)) ||
|
||||
(codeToolsEnabled &&
|
||||
providerSupportsBuiltinCodeExecution(
|
||||
externalProvider.providerType,
|
||||
externalSelection.modelId,
|
||||
externalProvider.baseUrl,
|
||||
))
|
||||
...(webSearchEnabledForThisTurn ||
|
||||
webFetchEnabledForThisTurn ||
|
||||
codeExecEnabledForThisTurn
|
||||
? {
|
||||
enable_tools: true,
|
||||
enabled_tools: [
|
||||
...(toolsEnabled &&
|
||||
providerSupportsBuiltinWebSearch(
|
||||
externalProvider.providerType,
|
||||
)
|
||||
? ["web_search"]
|
||||
: []),
|
||||
...(webSearchEnabledForThisTurn ? ["web_search"] : []),
|
||||
// Pair web_fetch with the Search pill on any
|
||||
// provider that ships it (Anthropic today). The
|
||||
// common workflow is "search returns URLs, fetch
|
||||
|
|
@ -1339,20 +1408,8 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
// surface a citation but cannot quote from the
|
||||
// page body, which is the whole point of the
|
||||
// tool. There is no separate UI toggle yet.
|
||||
...(toolsEnabled &&
|
||||
providerSupportsBuiltinWebFetch(
|
||||
externalProvider.providerType,
|
||||
)
|
||||
? ["web_fetch"]
|
||||
: []),
|
||||
...(codeToolsEnabled &&
|
||||
providerSupportsBuiltinCodeExecution(
|
||||
externalProvider.providerType,
|
||||
externalSelection.modelId,
|
||||
externalProvider.baseUrl,
|
||||
)
|
||||
? ["code_execution"]
|
||||
: []),
|
||||
...(webFetchEnabledForThisTurn ? ["web_fetch"] : []),
|
||||
...(codeExecEnabledForThisTurn ? ["code_execution"] : []),
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue