Skip to content

Commit 48d1fea

Browse files
committed
simplify implementation
1 parent 1fad2b7 commit 48d1fea

File tree

3 files changed

+28
-61
lines changed

3 files changed

+28
-61
lines changed

src/const.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,19 @@ export const ACTOR_ADDITIONAL_INSTRUCTIONS = `Never call/execute tool/Actor unle
5656

5757
export const TOOL_CACHE_MAX_SIZE = 500;
5858
export const TOOL_CACHE_TTL_SECS = 30 * 60;
59+
60+
export const ACTOR_PRICING_MODEL = {
61+
/** Rental actors */
62+
FLAT_PRICE_PER_MONTH: 'FLAT_PRICE_PER_MONTH',
63+
FREE: 'FREE',
64+
/** Pay per result (PPR) actors */
65+
PRICE_PER_DATASET_ITEM: 'PRICE_PER_DATASET_ITEM',
66+
/** Pay per event (PPE) actors */
67+
PAY_PER_EVENT: 'PAY_PER_EVENT',
68+
} as const;
69+
70+
/**
71+
* Used in search Actors tool to search above the input supplied limit,
72+
* so we can safely filter out rental Actors from the search and ensure we return some results.
73+
*/
74+
export const ACTOR_SEARCH_ABOVE_LIMIT = 50;

src/tools/store_collection.ts

Lines changed: 9 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { z } from 'zod';
44
import zodToJsonSchema from 'zod-to-json-schema';
55

66
import { ApifyClient } from '../apify-client.js';
7-
import { HelperTools } from '../const.js';
8-
import type { ActorStorePruned, ApifyStorePricingModel, HelperTool, PricingInfo, ToolEntry } from '../types.js';
7+
import { ACTOR_SEARCH_ABOVE_LIMIT, HelperTools } from '../const.js';
8+
import type { ActorPricingModel, ActorStorePruned, HelperTool, PricingInfo, ToolEntry } from '../types.js';
99

1010
function pruneActorStoreInfo(response: ActorStoreList): ActorStorePruned {
1111
const stats = response.stats || {};
@@ -38,10 +38,9 @@ export async function searchActorsByKeywords(
3838
apifyToken: string,
3939
limit: number | undefined = undefined,
4040
offset: number | undefined = undefined,
41-
pricingModel: ApifyStorePricingModel | undefined = undefined,
4241
): Promise<ActorStorePruned[]> {
4342
const client = new ApifyClient({ token: apifyToken });
44-
const results = await client.store().list({ search, limit, offset, pricingModel });
43+
const results = await client.store().list({ search, limit, offset });
4544
return results.items.map((x) => pruneActorStoreInfo(x));
4645
}
4746

@@ -70,7 +69,9 @@ export const searchActorsArgsSchema = z.object({
7069
});
7170

7271
/**
73-
* Filters out actors with the 'FLAT_PRICE_PER_MONTH' pricing model (rental actors).
72+
* Filters out actors with the 'FLAT_PRICE_PER_MONTH' pricing model (rental actors)..
73+
*
74+
* Returns new array of Actors excluding those with 'FLAT_PRICE_PER_MONTH' pricing model.
7475
*
7576
* @param actors - Array of ActorStorePruned objects to filter.
7677
* @returns Array of actors excluding those with 'FLAT_PRICE_PER_MONTH' pricing model.
@@ -80,48 +81,7 @@ function filterRentalActors(
8081
): ActorStorePruned[] {
8182
// Store list API does not support filtering by two pricing models at once,
8283
// so we filter the results manually after fetching them.
83-
return actors.filter((actor) => (actor.currentPricingInfo.pricingModel as ApifyStorePricingModel) !== 'FLAT_PRICE_PER_MONTH');
84-
}
85-
86-
/**
87-
* Fallback function to fetch actors if no rental actors are found.
88-
* Fetches both free and pay-per-result actors and merges them in a zig-zag order.
89-
*
90-
* @param search - Search keywords for actors.
91-
* @param apifyToken - Apify API token.
92-
* @param limit - Maximum number of actors to return.
93-
* @param offset - Number of actors to skip from the start.
94-
* @returns Array of ActorStorePruned objects, alternating between free and pay-per-result actors.
95-
*/
96-
async function getFallbackActors(
97-
search: string,
98-
apifyToken: string,
99-
limit: number | undefined,
100-
offset: number | undefined,
101-
): Promise<ActorStorePruned[]> {
102-
const freeActors = await searchActorsByKeywords(
103-
search,
104-
apifyToken,
105-
limit,
106-
offset,
107-
'FREE',
108-
);
109-
const payPerResultActors = await searchActorsByKeywords(
110-
search,
111-
apifyToken,
112-
limit,
113-
offset,
114-
'PRICE_PER_DATASET_ITEM',
115-
);
116-
const allActors: ActorStorePruned[] = [];
117-
// Push Actors in zig-zag order to ensure that we return all Actors
118-
// in relevant order.
119-
const maxLength = Math.max(freeActors?.length || 0, payPerResultActors?.length || 0);
120-
for (let i = 0; i < maxLength; i++) {
121-
if (freeActors && freeActors[i]) allActors.push(freeActors[i]);
122-
if (payPerResultActors && payPerResultActors[i]) allActors.push(payPerResultActors[i]);
123-
}
124-
return allActors;
84+
return actors.filter((actor) => (actor.currentPricingInfo.pricingModel as ActorPricingModel) !== 'FLAT_PRICE_PER_MONTH');
12585
}
12686

12787
/**
@@ -147,21 +107,10 @@ export const searchActors: ToolEntry = {
147107
let actors = await searchActorsByKeywords(
148108
parsed.search,
149109
apifyToken,
150-
parsed.limit,
110+
parsed.limit + ACTOR_SEARCH_ABOVE_LIMIT,
151111
parsed.offset,
152112
);
153-
actors = filterRentalActors(actors || []);
154-
if (actors.length === 0) {
155-
// If no non-rental actors found, search for free and pay-per-result actors directly
156-
// and sort them by total stars.
157-
// This is a fallback to ensure we return some results.
158-
actors = await getFallbackActors(
159-
parsed.search,
160-
apifyToken,
161-
parsed.limit,
162-
parsed.offset,
163-
);
164-
}
113+
actors = filterRentalActors(actors || []).slice(0, parsed.limit);
165114

166115
return { content: actors?.map((item) => ({ type: 'text', text: JSON.stringify(item) })) };
167116
},

src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
22
import type { ValidateFunction } from 'ajv';
33
import type { ActorDefaultRunOptions, ActorDefinition } from 'apify-client';
44

5+
import type { ACTOR_PRICING_MODEL } from './const.js';
56
import type { ActorsMcpServer } from './mcp/server.js';
67

78
export interface ISchemaProperties {
@@ -191,4 +192,5 @@ export interface ToolCacheEntry {
191192
tool: ToolEntry;
192193
}
193194

194-
export type ApifyStorePricingModel = 'FREE' | 'FLAT_PRICE_PER_MONTH' | 'PRICE_PER_DATASET_ITEM';
195+
// Utility type to get the union of values of an object type
196+
export type ActorPricingModel = (typeof ACTOR_PRICING_MODEL)[keyof typeof ACTOR_PRICING_MODEL];

0 commit comments

Comments
 (0)