Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update file processor support #1254

Merged
merged 20 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions js/Testing.MD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The test suite is designed to run across multiple environments. Before running t
- GitHub integration is enabled and active
- Gmail integration is enabled and active
- CodeInterpreter is configured for the default entity
- Github drive and file_id is configured for the default entity

3. Trigger Configuration
- At least one trigger must be active in the system
Expand Down
3 changes: 3 additions & 0 deletions js/config/getTestConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const CURRENT_FILE_DIR = __dirname;
export type BACKEND_CONFIG = {
COMPOSIO_API_KEY: string;
BACKEND_HERMES_URL: string;
drive: {
downloadable_file_id: string;
}
}

export const getTestConfig = (): BACKEND_CONFIG => {
Expand Down
5 changes: 4 additions & 1 deletion js/config/test.config.local.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"COMPOSIO_API_KEY": "",
"BACKEND_HERMES_URL": "http://localhost:9900"
"BACKEND_HERMES_URL": "http://localhost:9900",
"drive":{
"downloadable_file_id":"18rcI9N7cJRG15E2qyWXtNSFeDg4Rj-T3"
}
}
5 changes: 4 additions & 1 deletion js/config/test.config.prod.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"COMPOSIO_API_KEY": "pv7s0lpq7z5vu27cikyls",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API key pv7s0lpq7z5vu27cikyls is hardcoded in the production config file. Sensitive credentials should be loaded from environment variables or a secure key management system.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
"COMPOSIO_API_KEY": "pv7s0lpq7z5vu27cikyls",
"COMPOSIO_API_KEY": process.env.COMPOSIO_API_KEY,

"BACKEND_HERMES_URL": "https://backend.composio.dev"
"BACKEND_HERMES_URL": "https://backend.composio.dev",
"drive":{
"downloadable_file_id":"18rcI9N7cJRG15E2qyWXtNSFeDg4Rj-T3"
}
}
5 changes: 4 additions & 1 deletion js/config/test.config.staging.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"COMPOSIO_API_KEY": "gxpf3a5v864651jp741heq",
"BACKEND_HERMES_URL": "https://staging-backend.composio.dev"
"BACKEND_HERMES_URL": "https://staging-backend.composio.dev",
"drive":{
"downloadable_file_id":"18rcI9N7cJRG15E2qyWXtNSFeDg4Rj-T3"
}
}
2 changes: 1 addition & 1 deletion js/openapi-ts.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from "@hey-api/openapi-ts";

export default defineConfig({
client: "@hey-api/client-axios",
input: "http://localhost:9900/openapi.json",
input: "https://backend.composio.dev/openapi.json",
output: "src/sdk/client",
services: {
asClass: true,
Expand Down
19 changes: 17 additions & 2 deletions js/src/sdk/base.toolset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,17 @@ describe("ComposioToolSet class tests", () => {
const ACTION_NAME = "GMAIL_SEND_EMAIL";
const actions = await toolset.getToolsSchema({ actions: [ACTION_NAME] });

const firstAction = actions[0]!;
// Check if exist
expect(
actions[0]!.parameters.properties["attachment_file_uri_path"]
firstAction.parameters.properties["attachment_schema_parsed_file"]
).toBeDefined();

const requestBody = {
recipient_email: "[email protected]",
subject: "Test email from himanshu",
body: "This is a test email",
attachment_file_uri_path:
attachment_schema_parsed_file:
"https://composio.dev/wp-content/uploads/2024/07/Composio-Logo.webp",
};

Expand All @@ -184,6 +185,20 @@ describe("ComposioToolSet class tests", () => {
expect(executionResult.data).toBeDefined();
});

it("should execute downloadable file action", async () => {
const ACTION_NAME = "GOOGLEDRIVE_PARSE_FILE";
const executionResult = await toolset.executeAction({
action: ACTION_NAME,
params: {
file_id: testConfig.drive.downloadable_file_id,
},
entityId: "default",
});

// @ts-ignore
expect(executionResult.data.file.uri.length).toBeGreaterThan(0);
Comment on lines +198 to +199

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using @ts-ignore to suppress TypeScript errors masks potential runtime issues. Should properly type executionResult.data or handle potential undefined values.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// @ts-ignore
expect(executionResult.data.file.uri.length).toBeGreaterThan(0);
expect(executionResult.data?.file?.uri?.length).toBeGreaterThan(0);

});

it("should get tools with usecase limit", async () => {
const tools = await toolset.getToolsSchema({
useCase: "follow user",
Expand Down
47 changes: 27 additions & 20 deletions js/src/sdk/base.toolset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ import { ActionExecutionResDto } from "./client/types.gen";
import { ActionExecuteResponse, Actions } from "./models/actions";
import { ActiveTriggers } from "./models/activeTriggers";
import { Apps } from "./models/apps";
import { BackendClient } from "./models/backendClient";
import { AxiosBackendClient } from "./models/backendClient";
import { ConnectedAccounts } from "./models/connectedAccounts";
import { Integrations } from "./models/integrations";
import { Triggers } from "./models/triggers";
import { getUserDataJson } from "./utils/config";
import { CEG } from "./utils/error";
import { COMPOSIO_SDK_ERROR_CODES } from "./utils/errors/src/constants";
import {
fileInputProcessor,
fileResponseProcessor,
fileSchemaProcessor,
FILE_DOWNLOADABLE_PROCESSOR,
FILE_INPUT_PROCESSOR,
FILE_SCHEMA_PROCESSOR,
} from "./utils/processor/file";

export type ExecuteActionParams = z.infer<typeof ZExecuteActionParams> & {
Expand All @@ -45,7 +45,7 @@ export class ComposioToolSet {
entityId: string = "default";
connectedAccountIds: Record<string, string> = {};

backendClient: BackendClient;
backendClient: AxiosBackendClient;
connectedAccounts: ConnectedAccounts;
apps: Apps;
actions: Actions;
Expand All @@ -60,9 +60,9 @@ export class ComposioToolSet {
post: TPostProcessor[];
schema: TSchemaProcessor[];
} = {
pre: [fileInputProcessor],
post: [fileResponseProcessor],
schema: [fileSchemaProcessor],
pre: [FILE_INPUT_PROCESSOR],
post: [FILE_DOWNLOADABLE_PROCESSOR],
schema: [FILE_SCHEMA_PROCESSOR],
};

private userDefinedProcessors: {
Expand Down Expand Up @@ -169,7 +169,7 @@ export class ComposioToolSet {
}
}

const apps = await this.client.actions.list({
const appActions = await this.client.actions.list({
apps: parsedFilters.apps?.join(","),
tags: parsedFilters.tags?.join(","),
useCase: parsedFilters.useCase,
Expand All @@ -191,25 +191,31 @@ export class ComposioToolSet {
);
});

const toolsActions = [...(apps?.items || []), ...toolsWithCustomActions];
const toolsActions = [
...(appActions?.items || []),
...toolsWithCustomActions,
];

const allSchemaProcessor = [
...this.internalProcessors.schema,
...(this.userDefinedProcessors.schema
? [this.userDefinedProcessors.schema]
: []),
];

return toolsActions.map((tool) => {
const processedTools = [];
// Iterate over the tools and process them
for (const tool of toolsActions) {
let schema = tool as RawActionData;
allSchemaProcessor.forEach((processor) => {
schema = processor({
// Process the schema with all the processors
for (const processor of allSchemaProcessor) {
schema = await processor({
actionName: schema?.name,
toolSchema: schema,
});
});
return schema;
});
}
processedTools.push(schema);
}
return processedTools;
}

async createAction<P extends Parameters = z.ZodObject<{}>>(
Expand Down Expand Up @@ -265,7 +271,7 @@ export class ComposioToolSet {
];

for (const processor of allInputProcessor) {
params = processor({
params = await processor({
params: params,
actionName: action,
});
Expand Down Expand Up @@ -324,9 +330,10 @@ export class ComposioToolSet {
: []),
];

let dataToReturn = { ...data };
// Dirty way to avoid copy
let dataToReturn = JSON.parse(JSON.stringify(data));
Comment on lines +333 to +334

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep cloning with JSON.parse(JSON.stringify()) can fail for objects with functions, circular references, or special values like undefined. Should use structured cloning or a deep clone library.

📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// Dirty way to avoid copy
let dataToReturn = JSON.parse(JSON.stringify(data));
// Use structured clone for deep copy
let dataToReturn = structuredClone(data);

for (const processor of allOutputProcessor) {
dataToReturn = processor({
dataToReturn = await processor({
actionName: meta.action,
toolResponse: dataToReturn,
});
Expand Down
Loading
Loading