In many scenarios, the schemas, inputs, or outputs of tools may benefit from additional processing. This refinement step can significantly improve the quality and usability of your data. Here are three key use cases:
- Modifying Schema: Modify the tool schema like the description of the parameters or the default values. For example, if youโre manually providing certain values in input (like project_id), you can mark these fields as โnot requiredโ in the schema so the LLM knows it doesnโt need to ask for them.
- Modifying Inputs: Add values as inputs to avoid specifying them in the prompt. e.g., passing
project_id & team_id to the LINEAR_CREATE_LINEAR_ISSUE action.
- Modifying Outputs: Modify outputs to get the desired data. e.g., extracting
execution_id & issue_id from the response of LINEAR_CREATE_LINEAR_ISSUE action. Doing this can help keep the LLM context clean.
Composio empowers you with the ability to define custom functions as schema modifiers, input modifiers, or output modifiers.
These can be applied at two levels:
- App-level: Affects all actions within a specific tool.
- Action-level: Tailored processing for individual actions.
Install required libraries
pip install langchain langchain-openai composio-langchain
Import required libraries
from langchain.agents import create_openai_functions_agent, AgentExecutor
from langchain import hub
from langchain_openai import ChatOpenAI
from composio_langchain import ComposioToolSet, Action, App
Import Prompt template & Initialize ChatOpenAI & composio toolset client
prompt = hub.pull("hwchase17/openai-functions-agent")
llm = ChatOpenAI()
composio_toolset = ComposioToolSet()
Define a Custom Function to Modify Schema
This function will be used to modify the schema of the LINEAR_CREATE_LINEAR_ISSUE action, we get rid of the parameters project_id and team_id, later in the program we will pass these values as inputs to the action manually. The technical term for this is Action-level Schema Processing.def linear_schema_processor(schema: dict) -> dict:
# This way the agent doesn't expect a project and team ID to run the action
del schema['project_id']
del schema['team_id']
return schema
Define a Custom Function to Modify Input
This function will be used to modify the input data for the LINEAR_CREATE_LINEAR_ISSUE action. Here we have added the values for project_id and team_id parameters to the input data. By doing this, we can avoid specifying these values in the prompt and be sure that the agent uses the correct values. The technical term for this is Action-level Pre-Processing.def linear_pre_processor(input_data: dict) -> dict:
input_data['project_id'] = 'e708162b-9b1a-4901-ab93-0f0149f9d805'
input_data['team_id'] = '249ee4cc-7bbb-4ff1-adbe-d3ef2f3df94e'
return input_data
Define a Custom Function to Modify Output
This function will be used to modify the output data for the LINEAR_CREATE_LINEAR_ISSUE action. Here we are modifying the output to just return the action execution status successful & the issue_id, by doing this can keep the LLM context clean. The technical term for this is Action-level Post-Processing.def linear_post_processor(output_data: dict) -> dict:
output_data = {
'success': output_data['successfull'],
'issue_id': output_data['id'],
}
return output_data
Get Linear Action from Composio
When getting tools using the get_tools() method, we need to pass the processors parameter to specify the schema, pre-processing, and post-processing functions. In this example, weโre setting up an Action-level preprocessor by mapping the LINEAR_CREATE_LINEAR_ISSUE action to our linear_schema_processor, linear_pre_processor and linear_post_processor functions defined above respectively in schema, pre, and post processors.tools = composio_toolset.get_tools(
processors={
"schema": {
Action.LINEAR_CREATE_LINEAR_ISSUE: linear_schema_processor,
},
"pre": {
Action.LINEAR_CREATE_LINEAR_ISSUE: linear_pre_processor,
},
"post": {
Action.LINEAR_CREATE_LINEAR_ISSUE: linear_post_processor,
}
},
actions=[Action.LINEAR_CREATE_LINEAR_ISSUE]
)
Invoke the agent
task = "Create a Linear Issue to update the frontend"
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
agent_executor.invoke({"input": task})
Install required libraries
npm install composio-core @langchain/openai langchain @langchain/core
Import required libraries
import { ActionExecutionResDto, LangchainToolSet, RawActionData, TPostProcessor, TPreProcessor, TSchemaProcessor } from "composio-core";
import { ChatOpenAI } from "@langchain/openai";
import { createOpenAIFunctionsAgent, AgentExecutor } from "langchain/agents";
import { pull } from "langchain/hub";
import { ChatPromptTemplate } from "@langchain/core/prompts";
Initialize ChatOpenAI & LangChain toolset client
const llm = new ChatOpenAI({ apiKey: "<your-api-key>" });
const toolset = new LangchainToolSet({ apiKey: "<your-api-key>" });
Schema Modifier
This will be used to modify the schema of the LINEAR_CREATE_LINEAR_ISSUE action, we remove the parameters project_id and team_id as required fields, later in the program we will pass these values as inputs to the action manually. The technical term for this is Schema Processing.const schemaProcessor: TSchemaProcessor = ({
actionName,
toolSchema,
}: {
actionName: string;
toolSchema: RawActionData;
}) => {
const modifiedSchema = { ...toolSchema };
modifiedSchema.parameters = {
...modifiedSchema.parameters,
required: modifiedSchema.parameters?.required?.filter(
field => !['project_id', 'team_id'].includes(field)
) || []
};
return modifiedSchema;
};
Input Modifier
This will be used to modify the input data for the LINEAR_CREATE_LINEAR_ISSUE action. Here we have added the values for project_id and team_id parameters to the input data. By doing this, we can avoid specifying these values in the prompt and be sure that the agent uses the correct values. The technical term for this is Pre-Processing.const preProcessor: TPreProcessor = ({ params, actionName, appName }: {
params: Record<string, unknown>;
actionName: string;
appName: string;
}) => {
const modifiedParams = { ...params };
modifiedParams.project_id = "e708162b-9b1a-4901-ab93-0f0149f9d805";
modifiedParams.team_id = "249ee4cc-7bbb-4ff1-adbe-d3ef2f3df94e";
return modifiedParams;
}
Output Modifier
This will be used to modify the output data for the LINEAR_CREATE_LINEAR_ISSUE action. Here we are modifying the output to just return the action execution status successful & the issueId, by doing this can keep the LLM context clean. The technical term for this is Post-Processing.const postProcessor: TPostProcessor = ({ actionName, appName, toolResponse }: {
actionName: string;
appName: string;
toolResponse: ActionExecutionResDto;
}) => {
const issueId = toolResponse.data.id;
return { data: { id: issueId }, successful: true };
}
Add the Processors to Toolset & Execute the Agent
After creating the processors, we need to add them to the toolset using addSchemaProcessor, addPreProcessor, and addPostProcessor methods and then get the tools. Last we create the agent and execute it.async function main() {
toolset.addSchemaProcessor(schemaProcessor);
toolset.addPreProcessor(preProcessor);
toolset.addPostProcessor(postProcessor);
const tools = await toolset.getTools({
actions: ["LINEAR_CREATE_LINEAR_ISSUE"]
});
const prompt = (await pull(
"hwchase17/openai-functions-agent"
)) as ChatPromptTemplate;
const agent = await createOpenAIFunctionsAgent({
llm,
tools,
prompt,
});
const agentExecutor = new AgentExecutor({ agent, tools, verbose: true });
const response = await agentExecutor.invoke({ input: "Create an issue on linear to update the frontend to with new design and description 'to update the frontend with new design', estimate is 5 (L) & return the issue id" });
console.log(response);
}
main()
How to use processors at App-level?
Above we saw how to use processors at the Action-level, below is an example of how to use them at the App-level.
tools = composio_toolset.get_tools(
processors={
"pre": {
App.<app_name>: processor_function,
},
"post": {
App.<app_name>: processor_function,
},
"schema": {
App.<app_name>: processor_function,
},
},
apps=[App.<app_name>]
)
Ensure that your schema processing, preprocessing, and postprocessing functions are efficient and donโt introduce significant latency.