|
1 | | -import type { ActorInputSchema } from '../../types.js'; |
| 1 | +import { RAG_WEB_BROWSER, WEB_FETCH } from '../../const.js'; |
| 2 | +import type { ActorInputSchema, ToolDescriptionContext } from '../../types.js'; |
| 3 | +import { actorNameToToolName } from '../actor_tool_naming.js'; |
| 4 | + |
| 5 | +const RAG_WEB_BROWSER_ADDITIONAL_DESC = `Use this tool for generic web content: it searches the web by query and returns the text of the top results. |
| 6 | +This tool scrapes the data itself - it does NOT just find tools. |
| 7 | +
|
| 8 | +Examples of when to use: |
| 9 | +- General questions answered by web pages (e.g., "What's the weather in Prague?", "Latest news about the EU AI Act", "How does HTTP/3 work?") |
| 10 | +
|
| 11 | +For detailed data from a specific platform (e.g. Amazon products with prices, Instagram posts, Google Maps places with ratings), a specialized Actor from the Apify Store returns complete, structured results; this tool returns page text only.`; |
| 12 | + |
| 13 | +/** Follows RAG_WEB_BROWSER_ADDITIONAL_DESC on its own line when the session serves the apify/web-fetch tool. */ |
| 14 | +const RAG_WEB_BROWSER_WEB_FETCH_HINT = `When the user provides one specific URL and wants that page's full or verbatim content, prefer the dedicated ${WEB_FETCH} tool - this tool is for searching and scraping by query.`; |
| 15 | +/** |
| 16 | + * Appended to the `apify/web-fetch` Actor tool description. Client-agnostic on purpose: |
| 17 | + * no references to any specific client or its built-in tools, so the same text works |
| 18 | + * for every MCP client. |
| 19 | + */ |
| 20 | +const WEB_FETCH_ADDITIONAL_DESC = `Use this tool to fetch a specific http(s) URL and return its complete content (one URL per call; http and https only). |
| 21 | +It renders JavaScript and bypasses anti-bot protection, so it also retrieves pages where a plain HTTP fetch gets blocked, fails with an error such as 403 or 429, or returns incomplete content. |
| 22 | +The page comes back verbatim - full content, no summarization - as Markdown, plain text, HTML, the raw response body, or the list of links on the page. |
| 23 | +
|
| 24 | +Examples of when to use: |
| 25 | +- User provides a URL and wants its content, its data or links, or an answer that requires reading that page |
| 26 | +- A previous attempt to fetch a page was blocked, returned an error or CAPTCHA, or came back incomplete because the page needs JavaScript rendering |
| 27 | +- Verbatim page content is needed, e.g. for quoting, extraction, or archiving |
| 28 | +
|
| 29 | +This tool does not search the web - it needs a URL. To find pages by query, use a web search tool instead. |
| 30 | +If the exact URL cannot be fetched (e.g. a non-http(s) scheme), say so rather than silently substituting a different URL.`; |
| 31 | + |
| 32 | +/** |
| 33 | + * Appended to the `url` parameter description of the `apify/web-fetch` tool. Lives on the |
| 34 | + * parameter because that is what an agent reads while writing the argument: agents |
| 35 | + * silently rewrote ftp:// URLs to https:// instead of telling the user the scheme is |
| 36 | + * unsupported. |
| 37 | + */ |
| 38 | +const WEB_FETCH_URL_SCHEME_NOTE = |
| 39 | + 'http(s) URLs only - for any other scheme (e.g. ftp:), tell the user this tool cannot fetch it rather than substituting a different URL.'; |
2 | 40 |
|
3 | 41 | type DirectActorPolicyConfig = { |
| 42 | + /** Sent with the run input when the caller omits the field; the served schema shows it as the default. */ |
4 | 43 | defaults?: Readonly<Record<string, unknown>>; |
| 44 | + /** Left out of the served schema and refused by the call engine when sent. */ |
5 | 45 | deniedInputs?: readonly string[]; |
6 | 46 | /** Why, as served to the agent in the description and the refusal. */ |
7 | 47 | deniedInputsReason?: string; |
8 | 48 | }; |
9 | 49 |
|
10 | | -/** MCP-only contracts; the Actor's original API contract stays unchanged (#1574, #1575). */ |
11 | | -const DIRECT_ACTOR_POLICIES: Readonly<Record<string, DirectActorPolicyConfig>> = { |
| 50 | +type ActorToolPolicyConfig = { |
| 51 | + /** Direct tools only; `call-actor` and the widgets keep the Actor's own contract. */ |
| 52 | + directInput?: DirectActorPolicyConfig; |
| 53 | + /** Only these inputs are served; the others are hidden but still accepted and passed to the run. */ |
| 54 | + exposedInputs?: readonly string[]; |
| 55 | + inputDescriptionSuffixes?: Readonly<Record<string, string>>; |
| 56 | + buildDescription?: (context: ToolDescriptionContext) => string; |
| 57 | +}; |
| 58 | + |
| 59 | +/** Presentation rules preserve existing generic-call behavior; directInput is direct-tool-only. */ |
| 60 | +const ACTOR_TOOL_POLICIES: Readonly<Record<string, ActorToolPolicyConfig>> = { |
12 | 61 | 'apify/website-content-crawler': { |
13 | | - defaults: { respectRobotsTxtFile: true }, |
14 | | - deniedInputs: ['pageFunction', 'initialCookies'], |
15 | | - deniedInputsReason: |
16 | | - 'page code can act on the crawled sites and session cookies can give access to accounts, which a read-only tool must not allow', |
| 62 | + directInput: { |
| 63 | + defaults: { respectRobotsTxtFile: true }, |
| 64 | + deniedInputs: ['pageFunction', 'initialCookies'], |
| 65 | + deniedInputsReason: |
| 66 | + 'page code can act on the crawled sites and session cookies can give access to accounts, which a read-only tool must not allow', |
| 67 | + }, |
| 68 | + }, |
| 69 | + [RAG_WEB_BROWSER]: { |
| 70 | + exposedInputs: ['query', 'maxResults', 'outputFormats'], |
| 71 | + buildDescription: ({ hasTool }) => { |
| 72 | + let description = RAG_WEB_BROWSER_ADDITIONAL_DESC; |
| 73 | + if (hasTool(actorNameToToolName(WEB_FETCH))) description += `\n${RAG_WEB_BROWSER_WEB_FETCH_HINT}`; |
| 74 | + return description; |
| 75 | + }, |
| 76 | + }, |
| 77 | + [WEB_FETCH]: { |
| 78 | + inputDescriptionSuffixes: { url: WEB_FETCH_URL_SCHEME_NOTE }, |
| 79 | + buildDescription: () => WEB_FETCH_ADDITIONAL_DESC, |
17 | 80 | }, |
18 | 81 | }; |
19 | 82 |
|
| 83 | +export function getActorToolPolicy(actorFullName: string): ActorToolPolicyConfig | undefined { |
| 84 | + return ACTOR_TOOL_POLICIES[actorFullName]; |
| 85 | +} |
| 86 | + |
20 | 87 | export function getDirectActorPolicy(actorFullName: string): DirectActorPolicyConfig | undefined { |
21 | | - return DIRECT_ACTOR_POLICIES[actorFullName]; |
| 88 | + return getActorToolPolicy(actorFullName)?.directInput; |
22 | 89 | } |
23 | 90 |
|
24 | 91 | export function buildDirectActorContract(actorFullName: string, input: ActorInputSchema | undefined) { |
|
0 commit comments