-
Notifications
You must be signed in to change notification settings - Fork 5.5k
feat(ai): pipedream ai sdk #16699
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(ai): pipedream ai sdk #16699
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| OPENAI_API_KEY= | ||
|
|
||
| # Get these by following https://pipedream.com/docs/connect/managed-auth/quickstart/#getting-started | ||
| PIPEDREAM_CLIENT_ID= | ||
| PIPEDREAM_CLIENT_SECRET= | ||
| PIPEDREAM_PROJECT_ID= | ||
| PIPEDREAM_PROJECT_ENVIRONMENT= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| .env | ||
| node_modules | ||
| dist |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # @pipedream/ai | ||
|
|
||
| > This library is in alpha status. The API is subject to breaking changes. | ||
|
|
||
| Create a .env file based on the .env.example file. | ||
|
|
||
| Basic example: | ||
| ```ts | ||
| import { OpenAiTools } from "@pipedream/ai" | ||
| import { OpenAI } from "openai" | ||
|
|
||
| const openai = new OpenAI() | ||
|
|
||
| // Replace with a unique identifier for your user | ||
| const userId = <add user id here> | ||
|
|
||
| const openAiTools = new OpenAiTools(userId) | ||
| const tools = await openAiTools.getTools({ | ||
| app: "slack", | ||
| }) | ||
|
|
||
| const completion = await openai.chat.completions.create({ | ||
| messages: [ | ||
| { | ||
| role: "user", | ||
| content: "Send a joke to #random channel", | ||
| }, | ||
| ], | ||
| model: "gpt-4o", | ||
| tools, | ||
| }) | ||
|
|
||
| const results = await openAiTools.handleCompletion(completion) | ||
|
|
||
| console.log(JSON.stringify(results, null, 2)) | ||
|
|
||
| ``` |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "@pipedream/ai", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "module", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "version": "0.0.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Pipedream AI", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "main": "./dist/index.js", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "types": "./dist/index.d.ts", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "exports": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ".": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "import": "./dist/index.js", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "types": "./dist/index.d.ts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "files": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dist", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "package.json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "publishConfig": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "access": "public" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "scripts": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "build": "tsc" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@pipedream/sdk": "workspace:^", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "zod": "^3.24.4", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "zod-to-json-schema": "^3.24.5" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "devDependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "bun": "^1.2.13", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "openai": "^4.77.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Package.json needs additional metadata fields The package structure is well-defined with appropriate ESM configuration, entry points, and dependencies. However, several standard metadata fields are missing that would improve package discoverability and usability. Add the following missing fields to the package.json file: {
"name": "@pipedream/ai",
"type": "module",
"version": "0.0.1",
"description": "Pipedream AI",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/PipedreamHQ/pipedream.git",
+ "directory": "packages/ai"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist",
"package.json"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc"
},
"dependencies": {
"@pipedream/sdk": "workspace:^",
"zod": "^3.24.4",
"zod-to-json-schema": "^3.24.5"
},
"devDependencies": {
"bun": "^1.2.13",
"openai": "^4.77.0"
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| import { | ||
| ConfigurableProps, V1Component, | ||
| } from "@pipedream/sdk"; | ||
| import { | ||
| z, ZodRawShape, | ||
| } from "zod"; | ||
| import { | ||
| extractEnumValues, toNonEmptyTuple, | ||
| } from "./lib/helpers"; | ||
|
|
||
| export const configurablePropsToZod = ( | ||
| component: V1Component, | ||
| options?: { | ||
| asyncOptionsDescription?: string; | ||
| configureComponentDescription?: string; | ||
| configurableProps?: ConfigurableProps; | ||
| }, | ||
| ) => { | ||
| const schema: ZodRawShape = {}; | ||
|
|
||
| for (const cp of options?.configurableProps || | ||
| (component.configurable_props as ConfigurableProps)) { | ||
| if (cp.hidden) { | ||
| continue; | ||
| } | ||
|
|
||
| if (cp.type === "app") { | ||
| // XXX handle directly in implementation | ||
| continue; | ||
| } else if (cp.type === "string") { | ||
| if (cp.options && Array.isArray(cp.options) && cp.options.length > 0) { | ||
| const enumValues = toNonEmptyTuple(extractEnumValues(cp.options)); | ||
| if (enumValues) { | ||
| schema[cp.name] = z.enum(enumValues); | ||
| } else { | ||
| schema[cp.name] = z.string(); | ||
| } | ||
| } else { | ||
| schema[cp.name] = z.string(); | ||
| } | ||
| } else if (cp.type === "string[]") { | ||
| if (cp.options && Array.isArray(cp.options) && cp.options.length > 0) { | ||
| const enumValues = toNonEmptyTuple(extractEnumValues(cp.options)); | ||
| if (enumValues) { | ||
| schema[cp.name] = z.array(z.enum(enumValues)); | ||
| } else { | ||
| schema[cp.name] = z.array(z.string()); | ||
| } | ||
| } else { | ||
| schema[cp.name] = z.array(z.string()); | ||
| } | ||
| } else if (cp.type === "$.discord.channel") { | ||
| schema[cp.name] = z.string(); | ||
| } else if (cp.type === "$.discord.channel[]") { | ||
| schema[cp.name] = z.array(z.string()); | ||
| } else if (cp.type === "object") { | ||
| schema[cp.name] = z.object({}).passthrough(); | ||
| } else if (cp.type === "any") { | ||
| schema[cp.name] = z.any(); | ||
| } else if (cp.type === "integer") { | ||
| schema[cp.name] = z.number().int(); | ||
| } else if (cp.type === "integer[]") { | ||
| schema[cp.name] = z.array(z.number().int()); | ||
| } else if (cp.type === "boolean") { | ||
| schema[cp.name] = z.boolean(); | ||
| } else if (cp.type === "boolean[]") { | ||
| schema[cp.name] = z.array(z.boolean()); | ||
| // ignore alerts, as no user input required | ||
| } else { | ||
| console.error("unhandled type. Skipping", cp.name, cp.type); | ||
| } | ||
|
|
||
| if (schema[cp.name]) { | ||
| if (cp.optional) { | ||
| schema[cp.name] = schema[cp.name].optional().nullable(); | ||
| } | ||
|
|
||
| let description: string = cp.description || ""; | ||
|
|
||
| if (cp.hidden) { | ||
| description += | ||
| "\n\nIMPORTANT: This property is hidden. Do not configure it and leave it blank.\n"; | ||
| } | ||
|
|
||
| if (cp.remoteOptions) { | ||
| if (options?.asyncOptionsDescription) { | ||
| if (options.configureComponentDescription) { | ||
| description += `\n\n${options.asyncOptionsDescription}`; | ||
| } | ||
| } else { | ||
| if (options?.configureComponentDescription) { | ||
| description += `\n\n${options.configureComponentDescription}`; | ||
| } | ||
| } | ||
| // if (cp.name.includes("id")) { | ||
| // description += `\n\nIMPORTANT: An ID is required for this property. If you don't have the id and only have the name, use the "${CONFIGURE_COMPONENT_TOOL_NAME}" tool to get the values.`; | ||
| // } | ||
| } | ||
|
|
||
| if (description.trim()) { | ||
| schema[cp.name] = schema[cp.name].describe(description.trim()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return schema; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export { | ||
| OpenAiTools, | ||
| } from "./tool-sets/openai" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { ConfigurableProps } from "@pipedream/sdk"; | ||
|
|
||
| export const componentAppKey = (configuredProps: ConfigurableProps) => { | ||
| return configuredProps.find((prop) => prop.type === "app")?.app; | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { z } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const configSchema = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PIPEDREAM_CLIENT_ID: z.string().min(1, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "PIPEDREAM_CLIENT_ID is required", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PIPEDREAM_CLIENT_SECRET: z.string().min(1, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "PIPEDREAM_CLIENT_SECRET is required", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PIPEDREAM_PROJECT_ID: z.string().min(1, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "PIPEDREAM_PROJECT_ID is required", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PIPEDREAM_PROJECT_ENVIRONMENT: z.enum([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "development", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "production", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3
to
+17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add JSDoc comments for better documentation The schema definition lacks documentation that explains what each environment variable is used for. Add JSDoc comments to document the purpose of each environment variable: + /**
+ * Schema for validating required Pipedream environment variables
+ */
const configSchema = z.object({
+ /**
+ * The client ID for authenticating with Pipedream API
+ */
PIPEDREAM_CLIENT_ID: z.string().min(1, {
message: "PIPEDREAM_CLIENT_ID is required",
}),
+ /**
+ * The client secret for authenticating with Pipedream API
+ */
PIPEDREAM_CLIENT_SECRET: z.string().min(1, {
message: "PIPEDREAM_CLIENT_SECRET is required",
}),
+ /**
+ * The project ID where Pipedream components will be executed
+ */
PIPEDREAM_PROJECT_ID: z.string().min(1, {
message: "PIPEDREAM_PROJECT_ID is required",
}),
+ /**
+ * The environment (development or production) where Pipedream components will be executed
+ */
PIPEDREAM_PROJECT_ENVIRONMENT: z.enum([
"development",
"production",
]),
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const config = configSchema.parse(process?.env); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Config validation should be more robust The current implementation immediately parses environment variables on import, which will throw an error if any required variables are missing. Make the config validation more robust by providing a function that validates the config when called, with better error handling: - export const config = configSchema.parse(process?.env);
+ export function getConfig() {
+ try {
+ return configSchema.parse(process.env);
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ const missingVars = error.issues.map(issue => issue.path[0]).join(", ");
+ throw new Error(`Missing or invalid environment variables: ${missingVars}. Please check your .env file.`);
+ }
+ throw error;
+ }
+ }
+
+ // For backward compatibility
+ export const config = getConfig();🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type Config = z.infer<typeof configSchema>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| export function toNonEmptyTuple<T extends string>( | ||
| arr: T[], | ||
| ): [T, ...T[]] | undefined { | ||
| return arr.length > 0 | ||
| ? (arr as [T, ...T[]]) | ||
| : undefined; | ||
| } | ||
|
|
||
| type EnumLike = string | { value: string }; | ||
|
|
||
| export function extractEnumValues(values: EnumLike[]): string[] { | ||
| return values.map((v) => (typeof v === "string" | ||
| ? v | ||
| : v.value)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { createBackendClient } from "@pipedream/sdk"; | ||
| import { config } from "./config"; | ||
|
|
||
| export const pd = createBackendClient({ | ||
| credentials: { | ||
| clientId: config.PIPEDREAM_CLIENT_ID, | ||
| clientSecret: config.PIPEDREAM_CLIENT_SECRET, | ||
| }, | ||
| projectId: config.PIPEDREAM_PROJECT_ID, | ||
| environment: config.PIPEDREAM_PROJECT_ENVIRONMENT, | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,101 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { z } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { configurablePropsToZod } from "../configurablePropsToZod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { pd } from "../lib/pd-client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Account, V1Component, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@pipedream/sdk"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { componentAppKey } from "../lib/componentAppKey"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type Tool = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schema: z.ZodObject<z.ZodRawShape>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute: (args: Record<string, unknown>) => Promise<unknown>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class CoreTools { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userId: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tools: Tool[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor(userId: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.userId = userId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async getTools(options?: { app?: string; query?: string }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { data: components } = await pd.getComponents({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app: options?.app, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| q: options?.query, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const component of components) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.tools.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: component.name.replace(/[^a-zA-Z0-9_-]/g, "_"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: component.description, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schema: z.object(configurablePropsToZod(component)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| execute: (args) => this.executeTool(component, args), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.tools; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Prevent duplicate tools when fetching multiple times. The current implementation adds tools to the array without checking for duplicates, which could lead to duplicates if getTools is called multiple times. async getTools(options?: { app?: string; query?: string }) {
const { data: components } = await pd.getComponents({
app: options?.app,
q: options?.query,
});
+ // Clear existing tools or use a Set to prevent duplicates
+ this.tools = [];
for (const component of components) {
this.tools.push({
name: component.name.replace(/[^a-zA-Z0-9_-]/g, "_"),
description: component.description,
schema: z.object(configurablePropsToZod(component)),
execute: (args) => this.executeTool(component, args),
});
}
return this.tools;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async getTool(name: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.tools.find((tool) => tool.name === name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async executeTool(component: V1Component, args: Record<string, unknown>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const appKey = componentAppKey(component.configurable_props); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!appKey) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("App name not found"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authProvision = await this.getAuthProvision({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app: appKey, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uuid: this.userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof authProvision === "string") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return authProvision; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return pd.runAction({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actionId: component.key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| configuredProps: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...args, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [appKey]: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| authProvisionId: authProvision.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| externalUserId: this.userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async getAuthProvision({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uuid: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }): Promise<Account | string> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authProvisions = await pd.getAccounts({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| external_user_id: uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include_credentials: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authProvision = authProvisions.data.find((ap) => ap.healthy); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!authProvision) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token = await pd.createConnectToken({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| external_user_id: uuid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| webhook_uri: "https://eokyfjps7uqmmrk.m.pipedream.net", // https://pipedream.com/@pd/p_G6Ck6Mk/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The user MUST be shown the following URL so they can click on it to connect their account and you MUST NOT modify the URL or it will break: https://pipedream.com/_static/connect.html?token=${token.token}&connectLink=true&app=${encodeURIComponent(app)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return authProvision; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+74
to
+100
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainHardcoded webhook URI might be an issue. The webhook URI is hardcoded, which could be problematic if this code is deployed across different environments. 🏁 Script executed: #!/bin/bash
# Check if the webhook URI is referenced elsewhere or configurable
rg "eokyfjps7uqmmrk.m.pipedream.net" --type ts --type jsLength of output: 343 Extract the webhook URI into configuration Hardcoding the Pipedream webhook URL in multiple places makes it brittle across environments. Move this value into a shared configuration (e.g. an environment variable or central config service) and reference it where needed. Files to update:
Suggested change example: // config.ts
export const PIPEDREAM_WEBHOOK_URI = process.env.PIPEDREAM_WEBHOOK_URI!
// usage
const token = await pd.createConnectToken({
external_user_id: uuid,
webhook_uri: PIPEDREAM_WEBHOOK_URI,
});🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider making OpenAI a peer dependency
OpenAI is currently listed as a dev dependency, but the example in the README shows it being imported by the user.
Move the OpenAI package to peerDependencies to indicate to users that they need to install it themselves:
"dependencies": { "@pipedream/sdk": "workspace:^", "zod": "^3.24.4", "zod-to-json-schema": "^3.24.5" }, + "peerDependencies": { + "openai": "^4.77.0" + }, "devDependencies": { "bun": "^1.2.13", - "openai": "^4.77.0" }📝 Committable suggestion
🤖 Prompt for AI Agents