Skip to content

Commit 820cff8

Browse files
authored
feat: handle Apify specific input schema props (#196)
* move and split input schema properties transform logic * add apify specific fields and properties to the Actor input transform pipeline * finalize code * add glob description, enums and examples * add fields to resource url list * comment * move apify-properties.ts to utils/ dir * use structured close, readonly input
1 parent e277cca commit 820cff8

File tree

5 files changed

+767
-69
lines changed

5 files changed

+767
-69
lines changed

src/tools/actor.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,9 @@ import type { ProgressTracker } from '../utils/progress.js';
2323
import { getActorDefinition } from './build.js';
2424
import {
2525
actorNameToToolName,
26-
addEnumsToDescriptionsWithExamples,
27-
buildNestedProperties,
28-
encodeDotPropertyNames,
29-
filterSchemaProperties,
3026
fixedAjvCompile,
3127
getToolSchemaID,
32-
markInputPropertiesAsRequired,
33-
shortenProperties,
28+
transformActorInputSchemaProperties,
3429
} from './utils.js';
3530

3631
const ajv = new Ajv({ coerceTypes: 'array', strict: false });
@@ -136,12 +131,7 @@ export async function getNormalActorsAsTools(
136131
if (actorDefinitionPruned) {
137132
const schemaID = getToolSchemaID(actorDefinitionPruned.actorFullName);
138133
if (actorDefinitionPruned.input && 'properties' in actorDefinitionPruned.input && actorDefinitionPruned.input) {
139-
actorDefinitionPruned.input.properties = markInputPropertiesAsRequired(actorDefinitionPruned.input);
140-
actorDefinitionPruned.input.properties = buildNestedProperties(actorDefinitionPruned.input.properties);
141-
actorDefinitionPruned.input.properties = filterSchemaProperties(actorDefinitionPruned.input.properties);
142-
actorDefinitionPruned.input.properties = shortenProperties(actorDefinitionPruned.input.properties);
143-
actorDefinitionPruned.input.properties = addEnumsToDescriptionsWithExamples(actorDefinitionPruned.input.properties);
144-
actorDefinitionPruned.input.properties = encodeDotPropertyNames(actorDefinitionPruned.input.properties);
134+
actorDefinitionPruned.input.properties = transformActorInputSchemaProperties(actorDefinitionPruned.input);
145135
// Add schema $id, each valid JSON schema should have a unique $id
146136
// see https://json-schema.org/understanding-json-schema/basics#declaring-a-unique-identifier
147137
actorDefinitionPruned.input.$id = schemaID;

src/tools/utils.ts

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import type { ValidateFunction } from 'ajv';
22
import type Ajv from 'ajv';
33

44
import { ACTOR_ENUM_MAX_LENGTH, ACTOR_MAX_DESCRIPTION_LENGTH } from '../const.js';
5-
import type { IActorInputSchema, ISchemaProperties } from '../types.js';
5+
import type { ActorInputSchemaProperties, IActorInputSchema, ISchemaProperties } from '../types.js';
6+
import {
7+
addGlobsProperties,
8+
addKeyValueProperties,
9+
addProxyProperties,
10+
addPseudoUrlsProperties,
11+
addRequestListSourcesProperties,
12+
addResourcePickerProperties as addArrayResourcePickerProperties,
13+
} from '../utils/apify-properties.js';
614

715
export function actorNameToToolName(actorName: string): string {
816
return actorName
@@ -48,42 +56,22 @@ export function fixedAjvCompile(ajvInstance: Ajv, schema: object): ValidateFunct
4856
* @param {Record<string, ISchemaProperties>} properties - The input schema properties
4957
* @returns {Record<string, ISchemaProperties>} Modified properties with nested properties
5058
*/
51-
export function buildNestedProperties(properties: Record<string, ISchemaProperties>): Record<string, ISchemaProperties> {
59+
export function buildApifySpecificProperties(properties: Record<string, ISchemaProperties>): Record<string, ISchemaProperties> {
5260
const clonedProperties = { ...properties };
5361

5462
for (const [propertyName, property] of Object.entries(clonedProperties)) {
5563
if (property.type === 'object' && property.editor === 'proxy') {
56-
clonedProperties[propertyName] = {
57-
...property,
58-
properties: {
59-
...property.properties,
60-
useApifyProxy: {
61-
title: 'Use Apify Proxy',
62-
type: 'boolean',
63-
description: 'Whether to use Apify Proxy - ALWAYS SET TO TRUE.',
64-
default: true,
65-
examples: [true],
66-
},
67-
},
68-
required: ['useApifyProxy'],
69-
};
64+
clonedProperties[propertyName] = addProxyProperties(property);
7065
} else if (property.type === 'array' && property.editor === 'requestListSources') {
71-
clonedProperties[propertyName] = {
72-
...property,
73-
items: {
74-
...property.items,
75-
type: 'object',
76-
title: 'Request list source',
77-
description: 'Request list source',
78-
properties: {
79-
url: {
80-
title: 'URL',
81-
type: 'string',
82-
description: 'URL of the request list source',
83-
},
84-
},
85-
},
86-
};
66+
clonedProperties[propertyName] = addRequestListSourcesProperties(property);
67+
} else if (property.type === 'array' && property.editor === 'pseudoUrls') {
68+
clonedProperties[propertyName] = addPseudoUrlsProperties(property);
69+
} else if (property.type === 'array' && property.editor === 'globs') {
70+
clonedProperties[propertyName] = addGlobsProperties(property);
71+
} else if (property.type === 'array' && property.editor === 'keyValue') {
72+
clonedProperties[propertyName] = addKeyValueProperties(property);
73+
} else if (property.type === 'array' && property.editor === 'resourcePicker') {
74+
clonedProperties[propertyName] = addArrayResourcePickerProperties(property);
8775
}
8876
}
8977

@@ -92,9 +80,7 @@ export function buildNestedProperties(properties: Record<string, ISchemaProperti
9280

9381
/**
9482
* Filters schema properties to include only the necessary fields.
95-
*
9683
* This is done to reduce the size of the input schema and to make it more readable.
97-
*
9884
* @param properties
9985
*/
10086
export function filterSchemaProperties(properties: { [key: string]: ISchemaProperties }): {
@@ -113,19 +99,31 @@ export function filterSchemaProperties(properties: { [key: string]: ISchemaPrope
11399
items: property.items,
114100
required: property.required,
115101
};
102+
}
103+
return filteredProperties;
104+
}
105+
106+
/**
107+
* For array properties missing items.type, infers and sets the type using inferArrayItemType.
108+
* @param properties
109+
*/
110+
export function inferArrayItemsTypeIfMissing(properties: { [key: string]: ISchemaProperties }): {
111+
[key: string]: ISchemaProperties
112+
} {
113+
for (const [, property] of Object.entries(properties)) {
116114
if (property.type === 'array' && !property.items?.type) {
117115
const itemsType = inferArrayItemType(property);
118116
if (itemsType) {
119-
filteredProperties[key].items = {
120-
...filteredProperties[key].items,
121-
title: filteredProperties[key].title ?? 'Item',
122-
description: filteredProperties[key].description ?? 'Item',
117+
property.items = {
118+
...property.items,
119+
title: property.title ?? 'Item',
120+
description: property.description ?? 'Item',
123121
type: itemsType,
124122
};
125123
}
126124
}
127125
}
128-
return filteredProperties;
126+
return properties;
129127
}
130128

131129
/**
@@ -173,6 +171,7 @@ export function inferArrayItemType(property: ISchemaProperties): string | null {
173171
stringList: 'string',
174172
json: 'object',
175173
globs: 'object',
174+
select: 'string',
176175
};
177176
return editorTypeMap[editor] || null;
178177
}
@@ -279,3 +278,16 @@ export function decodeDotPropertyNames(properties: Record<string, unknown>): Rec
279278
}
280279
return decodedProperties;
281280
}
281+
282+
export function transformActorInputSchemaProperties(input: Readonly<IActorInputSchema>): ActorInputSchemaProperties {
283+
// Deep clone input to avoid mutating the original object
284+
const inputClone: IActorInputSchema = structuredClone(input);
285+
let transformedProperties = markInputPropertiesAsRequired(inputClone);
286+
transformedProperties = buildApifySpecificProperties(transformedProperties);
287+
transformedProperties = filterSchemaProperties(transformedProperties);
288+
transformedProperties = inferArrayItemsTypeIfMissing(transformedProperties);
289+
transformedProperties = shortenProperties(transformedProperties);
290+
transformedProperties = addEnumsToDescriptionsWithExamples(transformedProperties);
291+
transformedProperties = encodeDotPropertyNames(transformedProperties);
292+
return transformedProperties;
293+
}

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,5 @@ export type PromptBase = Prompt & {
279279
*/
280280
render: (args: Record<string, string>) => string;
281281
};
282+
283+
export type ActorInputSchemaProperties = Record<string, ISchemaProperties>;

0 commit comments

Comments
 (0)