@@ -3,10 +3,10 @@ import { z } from 'zod';
33import zodToJsonSchema from 'zod-to-json-schema' ;
44
55import { ApifyClient } from '../apify-client.js' ;
6- import { APIFY_STORE_URL , HelperTools } from '../const.js' ;
7- import type { ExtendedPricingInfo , IActorInputSchema , InternalTool , ToolEntry } from '../types.js' ;
6+ import { HelperTools } from '../const.js' ;
7+ import type { IActorInputSchema , InternalTool , ToolEntry } from '../types.js' ;
8+ import { formatActorToActorCard } from '../utils/actor-card.js' ;
89import { ajv } from '../utils/ajv.js' ;
9- import { getCurrentPricingInfo , pricingInfoToString } from '../utils/pricing-info.js' ;
1010import { filterSchemaProperties , shortenProperties } from './utils.js' ;
1111
1212const getActorDetailsToolArgsSchema = z . object ( {
@@ -15,31 +15,16 @@ const getActorDetailsToolArgsSchema = z.object({
1515 . describe ( `Actor ID or full name in the format "username/name", e.g., "apify/rag-web-browser".` ) ,
1616} ) ;
1717
18- // Helper function to format categories from uppercase with underscores to proper case
19- function formatCategories ( categories ?: string [ ] ) : string [ ] {
20- if ( ! categories ) return [ ] ;
21-
22- return categories . map ( ( category ) => {
23- const formatted = category
24- . toLowerCase ( )
25- . split ( '_' )
26- . map ( ( word ) => word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 ) )
27- . join ( ' ' ) ;
28- // Special case for MCP server, AI, and SEO tools
29- return formatted . replace ( 'Mcp Server' , 'MCP Server' ) . replace ( 'Ai' , 'AI' ) . replace ( 'Seo' , 'SEO' ) ;
30- } ) ;
31- }
32-
3318export const getActorDetailsTool : ToolEntry = {
3419 type : 'internal' ,
3520 tool : {
3621 name : HelperTools . ACTOR_GET_DETAILS ,
37- description : `Retrieve comprehensive details about an Actor using its ID or full name.\n`
38- + `This tool provides the Actor's title, description, URL, documentation (README ), input schema, categories, pricing, and usage statistics.\n`
39- + `Specify the Actor name in the format "username/name" (e.g. , "apify/rag-web-browser") .\n`
40- + `The response is formatted in markdown and should be rendered as-is .\n`
22+ description : `Get detailed information about an Actor by its ID or full name.\n`
23+ + `This tool returns title, description, URL, README (Actor's documentation ), input schema, and usage statistics. \n`
24+ + `The Actor name is always composed of "username/name", for example , "apify/rag-web-browser".\n`
25+ + `Returns Actor information which must be displayed in the same format .\n`
4126 + `USAGE:\n`
42- + `- Use when a user requests information about an Actor, such as its details, description, input schema, or documentation .\n`
27+ + `- Use when user asks about an Actor its details, description, input schema, etc .\n`
4328 + `EXAMPLES:\n`
4429 + `- user_input: How to use apify/rag-web-browser\n`
4530 + `- user_input: What is the input schema for apify/rag-web-browser` ,
@@ -69,35 +54,14 @@ export const getActorDetailsTool: ToolEntry = {
6954 inputSchema . properties = filterSchemaProperties ( inputSchema . properties ) ;
7055 inputSchema . properties = shortenProperties ( inputSchema . properties ) ;
7156
72- const currentPricingInfo = getCurrentPricingInfo ( actorInfo . pricingInfos || [ ] , new Date ( ) ) ;
73-
74- // Format categories for display
75- const formattedCategories = formatCategories ( actorInfo . categories ) ;
76-
77- // Note: In the public API, we are missing maintainedByApify property, so we cannot use it here.
78- // Note: Actor rating is not in public API, we need to add it (actorUtils.getActorReviewRatingNumber(actorId))
79- const actorFullName = `${ actorInfo . username } /${ actorInfo . name } ` ;
80- const markdownLines = [
81- `Actor details (always present Actor information in this format, always include URL):\n` ,
82- `# [${ actorInfo . title } ](${ APIFY_STORE_URL } /${ actorFullName } ) (${ actorFullName } )` ,
83- `**Developed by:** ${ actorInfo . username } Maintained by ${ actorInfo . username === 'apify' ? '(Apify)' : '(community)' } ` ,
84- `**Description:** ${ actorInfo . description || 'No description provided.' } ` ,
85- `**Categories:** ${ formattedCategories . length ? formattedCategories . join ( ', ' ) : 'Uncategorized' } ` ,
86- `**Pricing:** ${ pricingInfoToString ( currentPricingInfo as ( ExtendedPricingInfo | null ) ) } ` ,
87- `**Stats:** ${ actorInfo . stats . totalUsers . toLocaleString ( ) } total users, ${ actorInfo . stats . totalUsers30Days . toLocaleString ( ) } monthly users` ,
88- `Last modified: ${ actorInfo . modifiedAt . toISOString ( ) } ` ,
89- ] ;
90- if ( actorInfo . isDeprecated ) {
91- markdownLines . push ( '\n>This Actor is deprecated and may not be maintained anymore.' ) ;
92- }
93- const actorCard = markdownLines . join ( '\n' ) ;
57+ // Use the actor formatter to get the main actor details
58+ const actorCard = formatActorToActorCard ( actorInfo ) ;
9459
9560 return {
9661 content : [
9762 { type : 'text' , text : actorCard } ,
98- // LLM properly format Actor card, if README and input schema are separate text blocks
99- { type : 'text' , text : `**README**:\n\n${ buildInfo . actorDefinition . readme || 'No README provided.' } ` } ,
100- { type : 'text' , text : `**Input Schema**:\n\n${ JSON . stringify ( inputSchema , null , 0 ) } ` } ,
63+ { type : 'text' , text : `**README:**\n${ buildInfo . actorDefinition . readme || 'No README provided.' } ` } ,
64+ { type : 'text' , text : `**Input Schema:**\n${ JSON . stringify ( inputSchema , null , 0 ) } ` } ,
10165 ] ,
10266 } ;
10367 } ,
0 commit comments