@botpress/adk 1.16.7 → 1.18.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-init/agent-project-generator.d.ts +32 -8
- package/dist/agent-init/agent-project-generator.d.ts.map +1 -1
- package/dist/agent-init/index.d.ts +1 -0
- package/dist/agent-init/index.d.ts.map +1 -1
- package/dist/agent-project/agent-project.d.ts +13 -1
- package/dist/agent-project/agent-project.d.ts.map +1 -1
- package/dist/agent-project/agent-resolver.d.ts +4 -3
- package/dist/agent-project/agent-resolver.d.ts.map +1 -1
- package/dist/agent-project/config-writer.d.ts +33 -0
- package/dist/agent-project/config-writer.d.ts.map +1 -1
- package/dist/agent-project/dependencies-parser.d.ts.map +1 -1
- package/dist/agent-project/index.d.ts +1 -1
- package/dist/agent-project/index.d.ts.map +1 -1
- package/dist/agent-project/types.d.ts +48 -22
- package/dist/agent-project/types.d.ts.map +1 -1
- package/dist/agent-project/validation-errors.d.ts.map +1 -1
- package/dist/auth/credentials.d.ts +15 -1
- package/dist/auth/credentials.d.ts.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/bot-generator/dev-id-manager.d.ts.map +1 -1
- package/dist/bot-generator/generator.d.ts.map +1 -1
- package/dist/commands/base-command.d.ts.map +1 -1
- package/dist/commands/bp-add-command.d.ts.map +1 -1
- package/dist/commands/bp-build-command.d.ts.map +1 -1
- package/dist/commands/bp-chat-command.d.ts.map +1 -1
- package/dist/commands/bp-deploy-command.d.ts.map +1 -1
- package/dist/commands/bp-dev-command.d.ts +2 -2
- package/dist/commands/bp-dev-command.d.ts.map +1 -1
- package/dist/commands/opencode-command.d.ts +2 -2
- package/dist/commands/opencode-command.d.ts.map +1 -1
- package/dist/commands/opencode-config.d.ts +1 -1
- package/dist/commands/opencode-config.d.ts.map +1 -1
- package/dist/config/coerce-config-value.d.ts.map +1 -1
- package/dist/config/manager.d.ts +5 -5
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/eval/client.d.ts +8 -0
- package/dist/eval/client.d.ts.map +1 -1
- package/dist/eval/graders/index.d.ts +1 -0
- package/dist/eval/graders/index.d.ts.map +1 -1
- package/dist/eval/graders/llm.d.ts +6 -2
- package/dist/eval/graders/llm.d.ts.map +1 -1
- package/dist/eval/graders/response.d.ts +1 -0
- package/dist/eval/graders/response.d.ts.map +1 -1
- package/dist/eval/graders/state.d.ts +1 -1
- package/dist/eval/graders/state.d.ts.map +1 -1
- package/dist/eval/graders/tables.d.ts.map +1 -1
- package/dist/eval/graders/timing.d.ts +7 -0
- package/dist/eval/graders/timing.d.ts.map +1 -0
- package/dist/eval/graders/workflow.d.ts.map +1 -1
- package/dist/eval/index.d.ts +3 -3
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/loader.d.ts +2 -1
- package/dist/eval/loader.d.ts.map +1 -1
- package/dist/eval/runner.d.ts +1 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/traces.d.ts.map +1 -1
- package/dist/eval/types.d.ts +62 -4
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/file-watcher/watcher.d.ts +9 -0
- package/dist/file-watcher/watcher.d.ts.map +1 -1
- package/dist/generators/client-wrapper.d.ts.map +1 -1
- package/dist/generators/conversation-types.d.ts.map +1 -1
- package/dist/generators/integration-types.d.ts.map +1 -1
- package/dist/generators/interface-types.d.ts.map +1 -1
- package/dist/generators/plugin-types.d.ts.map +1 -1
- package/dist/generators/table-types.d.ts.map +1 -1
- package/dist/generators/tests.d.ts.map +1 -1
- package/dist/generators/workflow-types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1194 -594
- package/dist/index.js.map +62 -61
- package/dist/integrations/checker.d.ts +2 -2
- package/dist/integrations/checker.d.ts.map +1 -1
- package/dist/integrations/config-utils.d.ts +4 -3
- package/dist/integrations/config-utils.d.ts.map +1 -1
- package/dist/integrations/operations.d.ts +19 -6
- package/dist/integrations/operations.d.ts.map +1 -1
- package/dist/integrations/types.d.ts +1 -1
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/interfaces/manager.d.ts.map +1 -1
- package/dist/interfaces/types.d.ts +1 -1
- package/dist/interfaces/types.d.ts.map +1 -1
- package/dist/knowledge/manager.d.ts.map +1 -1
- package/dist/plugins/types.d.ts +1 -1
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/preflight/agent-config-sync.d.ts +2 -1
- package/dist/preflight/agent-config-sync.d.ts.map +1 -1
- package/dist/preflight/checker.d.ts.map +1 -1
- package/dist/preflight/types.d.ts +8 -8
- package/dist/preflight/types.d.ts.map +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/script-runner.d.ts +1 -1
- package/dist/runner/script-runner.d.ts.map +1 -1
- package/dist/tables/table-manager.d.ts.map +1 -1
- package/dist/tables/types.d.ts +2 -1
- package/dist/tables/types.d.ts.map +1 -1
- package/dist/templates/README.md +101 -0
- package/dist/templates/blank/README.md +36 -0
- package/dist/templates/blank/agent.config.ts +49 -0
- package/dist/templates/blank/package.json +17 -0
- package/dist/templates/blank/src/actions/index.ts +19 -0
- package/dist/templates/blank/src/conversations/index.ts +16 -0
- package/dist/templates/blank/src/knowledge/index.ts +17 -0
- package/dist/templates/blank/src/tables/index.ts +19 -0
- package/dist/templates/blank/src/triggers/index.ts +20 -0
- package/dist/templates/blank/src/workflows/index.ts +23 -0
- package/dist/templates/blank/tsconfig.json +22 -0
- package/dist/templates/crm-enrichment/README.md +85 -0
- package/dist/templates/crm-enrichment/agent.config.ts +33 -0
- package/dist/templates/crm-enrichment/package.json +17 -0
- package/dist/templates/crm-enrichment/src/actions/enrich-contact.ts +81 -0
- package/dist/templates/crm-enrichment/src/conversations/index.ts +14 -0
- package/dist/templates/crm-enrichment/src/knowledge/index.ts +17 -0
- package/dist/templates/crm-enrichment/src/tables/contacts.ts +43 -0
- package/dist/templates/crm-enrichment/src/triggers/daily-enrichment.ts +30 -0
- package/dist/templates/crm-enrichment/src/workflows/enrichment-pipeline.ts +171 -0
- package/dist/templates/crm-enrichment/tsconfig.json +22 -0
- package/dist/templates/hello-world/README.md +46 -0
- package/dist/templates/hello-world/agent.config.ts +48 -0
- package/dist/templates/hello-world/package.json +17 -0
- package/dist/templates/hello-world/src/actions/index.ts +19 -0
- package/dist/templates/hello-world/src/conversations/index.ts +10 -0
- package/dist/templates/hello-world/src/knowledge/index.ts +17 -0
- package/dist/templates/hello-world/src/tables/index.ts +19 -0
- package/dist/templates/hello-world/src/triggers/index.ts +20 -0
- package/dist/templates/hello-world/src/workflows/index.ts +23 -0
- package/dist/templates/hello-world/tsconfig.json +22 -0
- package/dist/templates/knowledge-assistant/README.md +66 -0
- package/dist/templates/knowledge-assistant/agent.config.ts +26 -0
- package/dist/templates/knowledge-assistant/package.json +17 -0
- package/dist/templates/knowledge-assistant/src/actions/index.ts +19 -0
- package/dist/templates/knowledge-assistant/src/actions/search-docs.ts +50 -0
- package/dist/templates/knowledge-assistant/src/conversations/index.ts +33 -0
- package/dist/templates/knowledge-assistant/src/knowledge/docs.ts +23 -0
- package/dist/templates/knowledge-assistant/src/knowledge/getting-started.md +49 -0
- package/dist/templates/knowledge-assistant/src/tables/index.ts +21 -0
- package/dist/templates/knowledge-assistant/src/triggers/index.ts +17 -0
- package/dist/templates/knowledge-assistant/src/workflows/index.ts +28 -0
- package/dist/templates/knowledge-assistant/tsconfig.json +22 -0
- package/dist/templates/slack-triage/README.md +74 -0
- package/dist/templates/slack-triage/agent.config.ts +35 -0
- package/dist/templates/slack-triage/evals/triage-basic.eval.ts +55 -0
- package/dist/templates/slack-triage/package.json +17 -0
- package/dist/templates/slack-triage/src/actions/classify-request.ts +65 -0
- package/dist/templates/slack-triage/src/conversations/slack-dm.ts +72 -0
- package/dist/templates/slack-triage/src/knowledge/team-directory.md +33 -0
- package/dist/templates/slack-triage/src/tables/routing-rules.ts +38 -0
- package/dist/templates/slack-triage/src/triggers/new-message.ts +44 -0
- package/dist/templates/slack-triage/src/workflows/triage-flow.ts +104 -0
- package/dist/templates/slack-triage/tsconfig.json +22 -0
- package/dist/templates/template.config.json +47 -0
- package/dist/utils/json-ordering.d.ts +5 -4
- package/dist/utils/json-ordering.d.ts.map +1 -1
- package/package.json +31 -38
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// import { Trigger } from '@botpress/runtime'
|
|
2
|
+
//
|
|
3
|
+
// /**
|
|
4
|
+
// * An event subscription that fires automatically when the specified events occur.
|
|
5
|
+
// * Bot events: 'register', 'message.created', 'conversation.started', 'conversation.ended',
|
|
6
|
+
// * 'user.created', 'workflow.started', 'workflow.completed', 'workflow.failed'
|
|
7
|
+
// * Webchat: 'webchat:conversationStarted', 'webchat:trigger'
|
|
8
|
+
// * Slack: 'slack:reactionAdded', 'slack:memberJoinedChannel'
|
|
9
|
+
// * Linear: 'linear:issueCreated', 'linear:issueUpdated'
|
|
10
|
+
// * GitHub: 'github:issueOpened', 'github:pullRequestMerged'
|
|
11
|
+
// * Run `adk info <integration>` to see all events for an integration.
|
|
12
|
+
// */
|
|
13
|
+
// export default new Trigger({
|
|
14
|
+
// name: 'onRegister',
|
|
15
|
+
// description: 'Runs on bot startup to initialize state',
|
|
16
|
+
// events: ['register'],
|
|
17
|
+
// handler: async () => {
|
|
18
|
+
// console.log('Bot started!')
|
|
19
|
+
// },
|
|
20
|
+
// })
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// import { Workflow, z } from '@botpress/runtime'
|
|
2
|
+
//
|
|
3
|
+
// /**
|
|
4
|
+
// * A multi-step, resumable process that persists state across executions.
|
|
5
|
+
// * Each `step()` call is checkpointed — safe to retry on failure.
|
|
6
|
+
// * Use `step.sleep()`, `step.request()`, or `step.listen()` to pause execution.
|
|
7
|
+
// * Start from a trigger or conversation via `MyWorkflow.start({ input })`.
|
|
8
|
+
// */
|
|
9
|
+
// export const MyWorkflow = new Workflow({
|
|
10
|
+
// name: 'myWorkflow',
|
|
11
|
+
// input: z.object({
|
|
12
|
+
// data: z.string().describe('Input data to process'),
|
|
13
|
+
// }),
|
|
14
|
+
// output: z.object({
|
|
15
|
+
// result: z.string().describe('The processed result'),
|
|
16
|
+
// }),
|
|
17
|
+
// handler: async ({ input, step }) => {
|
|
18
|
+
// const result = await step('process', async () => {
|
|
19
|
+
// return `Processed: ${input.data}`
|
|
20
|
+
// })
|
|
21
|
+
// return { result }
|
|
22
|
+
// },
|
|
23
|
+
// })
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": ".",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"paths": {
|
|
17
|
+
"@botpress/runtime/_types/*": ["./.adk/*-types"]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*", ".adk/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A backend CRM enrichment agent built with the Botpress ADK. This agent runs scheduled workflows to classify, score, and enrich contacts in a table using AI -- no chat interface required.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
1. **Contacts table** (`src/tables/contacts.ts`) stores raw and enriched contact data.
|
|
8
|
+
2. **Enrich action** (`src/actions/enrich-contact.ts`) uses Zai to classify a single contact's use case, industry, and lead score.
|
|
9
|
+
3. **Enrichment pipeline** (`src/workflows/enrichment-pipeline.ts`) fetches unenriched contacts, enriches each one, and writes results back.
|
|
10
|
+
4. **Daily trigger** (`src/triggers/daily-enrichment.ts`) kicks off the pipeline on a schedule and on initial bot registration.
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
1. Install dependencies:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
{{packageManager}} install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. Start development server:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
adk dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. Add some contacts to the `ContactsTable` (via `adk run` script or the dev UI), then watch the enrichment pipeline process them.
|
|
27
|
+
|
|
28
|
+
4. Deploy your agent:
|
|
29
|
+
```bash
|
|
30
|
+
adk deploy
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Customizing the Enrichment Logic
|
|
34
|
+
|
|
35
|
+
The core AI classification lives in `src/actions/enrich-contact.ts`. You can customize:
|
|
36
|
+
|
|
37
|
+
- **Use case categories** -- edit the `useCase` enum in the Zai extract schema to match your business.
|
|
38
|
+
- **Industry list** -- expand or narrow the `industry` enum.
|
|
39
|
+
- **Scoring criteria** -- change the `score` enum and the `instructions` string to reflect your ideal customer profile.
|
|
40
|
+
- **Additional fields** -- add more columns to the table and more fields to the extract schema.
|
|
41
|
+
|
|
42
|
+
## Scheduling
|
|
43
|
+
|
|
44
|
+
The workflow runs on a cron schedule defined in `src/workflows/enrichment-pipeline.ts`:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
schedule: '0 9 * * *' // Every day at 9:00 AM UTC
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Common alternatives:
|
|
51
|
+
|
|
52
|
+
- `"0 */6 * * *"` -- every 6 hours
|
|
53
|
+
- `"0 9 * * 1-5"` -- weekday mornings only
|
|
54
|
+
- `"*/30 * * * *"` -- every 30 minutes
|
|
55
|
+
|
|
56
|
+
The trigger in `src/triggers/daily-enrichment.ts` also fires on `register` (bot startup) so the pipeline runs once immediately during development.
|
|
57
|
+
|
|
58
|
+
## Adding Integrations
|
|
59
|
+
|
|
60
|
+
This template starts with no integrations. To extend it:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Send enrichment reports to Slack
|
|
64
|
+
adk add slack@latest
|
|
65
|
+
|
|
66
|
+
# Sync contacts from a CRM
|
|
67
|
+
adk add salesforce@latest
|
|
68
|
+
|
|
69
|
+
# Send email summaries
|
|
70
|
+
adk add sendgrid@latest
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Project Structure
|
|
74
|
+
|
|
75
|
+
- `src/tables/` -- ContactsTable schema
|
|
76
|
+
- `src/actions/` -- AI enrichment action
|
|
77
|
+
- `src/workflows/` -- Enrichment pipeline workflow
|
|
78
|
+
- `src/triggers/` -- Scheduled trigger
|
|
79
|
+
- `src/conversations/` -- Unused (backend agent)
|
|
80
|
+
- `src/knowledge/` -- Unused (no RAG needed)
|
|
81
|
+
|
|
82
|
+
## Learn More
|
|
83
|
+
|
|
84
|
+
- [ADK Documentation](https://botpress.com/docs/adk)
|
|
85
|
+
- [Botpress Platform](https://botpress.com)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z, defineConfig } from '@botpress/runtime'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
name: '{{projectName}}',
|
|
5
|
+
description:
|
|
6
|
+
'A backend agent that enriches CRM contacts with AI-driven classification, scoring, and industry detection',
|
|
7
|
+
|
|
8
|
+
defaultModels: {
|
|
9
|
+
autonomous: 'openai:gpt-4.1-mini-2025-04-14',
|
|
10
|
+
zai: 'openai:gpt-4.1-2025-04-14',
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Bot-level persistent state — tracks enrichment run metadata across executions.
|
|
14
|
+
bot: {
|
|
15
|
+
state: z.object({
|
|
16
|
+
lastEnrichmentRunAt: z.string().optional().describe('ISO timestamp of the last completed enrichment run'),
|
|
17
|
+
totalContactsEnriched: z.number().default(0).describe('Cumulative count of contacts enriched across all runs'),
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Per-user state is unused in this backend-only agent.
|
|
22
|
+
user: {
|
|
23
|
+
state: z.object({}),
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// No integration dependencies — this is a pure backend workflow agent.
|
|
27
|
+
// If you want to send enrichment reports to Slack or email, add integrations here:
|
|
28
|
+
// adk add slack@latest
|
|
29
|
+
// adk add sendgrid@latest
|
|
30
|
+
dependencies: {
|
|
31
|
+
integrations: {},
|
|
32
|
+
},
|
|
33
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A CRM enrichment agent built with the ADK — scheduled workflows that classify and score contacts using AI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "adk dev",
|
|
8
|
+
"build": "adk build",
|
|
9
|
+
"deploy": "adk deploy"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@botpress/runtime": "^{{runtimeVersion}}"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.9.3"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Action, z, adk } from '@botpress/runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enriches a single contact using AI classification.
|
|
5
|
+
*
|
|
6
|
+
* Given a contact's name, email, and company, this action uses Zai to extract:
|
|
7
|
+
* - industry: what sector the company operates in
|
|
8
|
+
* - useCase: what they would most likely use the product for
|
|
9
|
+
* - score: a lead quality rating (high / medium / low)
|
|
10
|
+
*
|
|
11
|
+
* Customization points:
|
|
12
|
+
* - Change the enum values to match your business categories.
|
|
13
|
+
* - Edit the `instructions` to reflect your ideal customer profile.
|
|
14
|
+
* - Add more extracted fields (e.g. companySize, region, buyerPersona).
|
|
15
|
+
*/
|
|
16
|
+
export const enrichContact = new Action({
|
|
17
|
+
name: 'enrichContact',
|
|
18
|
+
description: 'Classify a CRM contact using AI to determine industry, use case, and lead score',
|
|
19
|
+
|
|
20
|
+
input: z.object({
|
|
21
|
+
name: z.string().describe('Full name of the contact'),
|
|
22
|
+
email: z.string().describe('Contact email address'),
|
|
23
|
+
company: z.string().describe('Company or organization name'),
|
|
24
|
+
}),
|
|
25
|
+
|
|
26
|
+
output: z.object({
|
|
27
|
+
industry: z.string().describe('Classified industry'),
|
|
28
|
+
useCase: z.string().describe('Classified use case'),
|
|
29
|
+
score: z.enum(['high', 'medium', 'low']).describe('Lead quality score'),
|
|
30
|
+
}),
|
|
31
|
+
|
|
32
|
+
async handler({ input }) {
|
|
33
|
+
// Build a text summary for the AI to classify.
|
|
34
|
+
const contactSummary = [`Name: ${input.name}`, `Email: ${input.email}`, `Company: ${input.company}`].join('\n')
|
|
35
|
+
|
|
36
|
+
// Use Zai extract to pull structured classification from the contact info.
|
|
37
|
+
// The AI infers industry, use case, and score from the name, email domain, and company.
|
|
38
|
+
const enriched = await adk.zai.extract(
|
|
39
|
+
contactSummary,
|
|
40
|
+
z.object({
|
|
41
|
+
industry: z
|
|
42
|
+
.enum([
|
|
43
|
+
'Technology',
|
|
44
|
+
'Healthcare',
|
|
45
|
+
'Finance',
|
|
46
|
+
'Retail',
|
|
47
|
+
'Education',
|
|
48
|
+
'Manufacturing',
|
|
49
|
+
'Media',
|
|
50
|
+
'Government',
|
|
51
|
+
'Other',
|
|
52
|
+
])
|
|
53
|
+
.describe('The primary industry the company operates in'),
|
|
54
|
+
|
|
55
|
+
useCase: z
|
|
56
|
+
.enum([
|
|
57
|
+
'Customer Support',
|
|
58
|
+
'Sales Automation',
|
|
59
|
+
'Internal Helpdesk',
|
|
60
|
+
'Lead Generation',
|
|
61
|
+
'Knowledge Management',
|
|
62
|
+
'Other',
|
|
63
|
+
])
|
|
64
|
+
.describe('The most likely use case for a conversational AI product'),
|
|
65
|
+
|
|
66
|
+
score: z.enum(['high', 'medium', 'low']).describe('Lead quality score based on company fit and likely intent'),
|
|
67
|
+
}),
|
|
68
|
+
{
|
|
69
|
+
instructions: [
|
|
70
|
+
'Classify this CRM contact based on their name, email domain, and company.',
|
|
71
|
+
'For industry: infer from the company name and email domain.',
|
|
72
|
+
'For useCase: estimate the most likely reason they would adopt a conversational AI platform.',
|
|
73
|
+
"For score: rate as 'high' if the company is mid-market or enterprise in a strong vertical,",
|
|
74
|
+
"'medium' for smaller companies or less obvious fit, 'low' for generic or unclear profiles.",
|
|
75
|
+
].join(' '),
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return enriched
|
|
80
|
+
},
|
|
81
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// This template is a backend-only agent — no conversation handler is needed.
|
|
2
|
+
// If you want to add a chat interface (e.g. to let users query enriched contacts),
|
|
3
|
+
// uncomment below and install a channel integration: adk add webchat@latest
|
|
4
|
+
//
|
|
5
|
+
// import { Conversation } from '@botpress/runtime'
|
|
6
|
+
//
|
|
7
|
+
// export default new Conversation({
|
|
8
|
+
// channel: '*',
|
|
9
|
+
// handler: async ({ execute }) => {
|
|
10
|
+
// await execute({
|
|
11
|
+
// instructions: 'You are a CRM assistant. Help users look up enriched contacts.',
|
|
12
|
+
// })
|
|
13
|
+
// },
|
|
14
|
+
// })
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// This template does not use a knowledge base.
|
|
2
|
+
// If you want to ground the enrichment logic with domain-specific documents
|
|
3
|
+
// (e.g. an ICP definition, industry taxonomy, or company database), you can
|
|
4
|
+
// add a knowledge base here and reference it in the enrichment action.
|
|
5
|
+
//
|
|
6
|
+
// import { Knowledge, DataSource } from '@botpress/runtime'
|
|
7
|
+
//
|
|
8
|
+
// const icpDocs = DataSource.Directory.fromPath('src/knowledge', {
|
|
9
|
+
// id: 'icp-docs',
|
|
10
|
+
// filter: (filePath) => filePath.endsWith('.md') || filePath.endsWith('.pdf'),
|
|
11
|
+
// })
|
|
12
|
+
//
|
|
13
|
+
// export const IcpKnowledge = new Knowledge({
|
|
14
|
+
// name: 'icp-knowledge',
|
|
15
|
+
// description: 'Ideal Customer Profile definitions and industry taxonomy',
|
|
16
|
+
// sources: [icpDocs],
|
|
17
|
+
// })
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Table, z } from '@botpress/runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ContactsTable stores CRM contacts along with their AI-enriched metadata.
|
|
5
|
+
*
|
|
6
|
+
* Columns like `name` and `company` are searchable so you can use semantic search
|
|
7
|
+
* to find contacts by fuzzy name/company queries.
|
|
8
|
+
*
|
|
9
|
+
* The enrichment fields (`useCase`, `industry`, `score`, `enrichedAt`) start empty
|
|
10
|
+
* and are populated by the enrichment pipeline workflow.
|
|
11
|
+
*
|
|
12
|
+
* To customize: add columns for phone, title, source, deal stage, etc.
|
|
13
|
+
*/
|
|
14
|
+
export const ContactsTable = new Table({
|
|
15
|
+
name: 'ContactsTable',
|
|
16
|
+
description: 'CRM contacts with AI-enriched classification data',
|
|
17
|
+
|
|
18
|
+
columns: {
|
|
19
|
+
// -- Core contact fields --
|
|
20
|
+
|
|
21
|
+
name: {
|
|
22
|
+
searchable: true,
|
|
23
|
+
schema: z.string().describe('Full name of the contact'),
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
email: z.string().describe('Contact email address'),
|
|
27
|
+
|
|
28
|
+
company: {
|
|
29
|
+
searchable: true,
|
|
30
|
+
schema: z.string().describe('Company or organization name'),
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// -- AI-enriched fields (populated by the enrichment pipeline) --
|
|
34
|
+
|
|
35
|
+
industry: z.string().optional().describe('AI-classified industry (e.g. Technology, Healthcare, Finance)'),
|
|
36
|
+
|
|
37
|
+
useCase: z.string().optional().describe('AI-classified use case (e.g. Customer Support, Sales Automation)'),
|
|
38
|
+
|
|
39
|
+
score: z.string().optional().describe('AI-assigned lead score: high, medium, or low'),
|
|
40
|
+
|
|
41
|
+
enrichedAt: z.string().optional().describe('ISO timestamp of when this contact was last enriched'),
|
|
42
|
+
},
|
|
43
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Trigger } from '@botpress/runtime'
|
|
2
|
+
import { EnrichmentPipeline } from '../workflows/enrichment-pipeline'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fires on bot registration (startup) to kick off an initial enrichment run.
|
|
6
|
+
*
|
|
7
|
+
* The enrichment pipeline also has its own cron schedule ("0 9 * * *"),
|
|
8
|
+
* which handles recurring daily runs automatically. This trigger ensures the
|
|
9
|
+
* pipeline runs once immediately when the bot first starts -- useful during
|
|
10
|
+
* development and after fresh deployments.
|
|
11
|
+
*
|
|
12
|
+
* To add more trigger events:
|
|
13
|
+
* - "user.created" -- enrich new contacts as they arrive
|
|
14
|
+
* - Custom events from integrations (e.g. "salesforce:contactCreated")
|
|
15
|
+
*/
|
|
16
|
+
export default new Trigger({
|
|
17
|
+
name: 'dailyEnrichment',
|
|
18
|
+
description: 'Starts the enrichment pipeline on bot registration for an initial run',
|
|
19
|
+
events: ['register'],
|
|
20
|
+
|
|
21
|
+
handler: async () => {
|
|
22
|
+
console.log('Bot registered. Starting initial enrichment pipeline run...')
|
|
23
|
+
|
|
24
|
+
const instance = await EnrichmentPipeline.start({
|
|
25
|
+
batchSize: 50,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
console.log(`Enrichment pipeline started: ${instance.id}`)
|
|
29
|
+
},
|
|
30
|
+
})
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { bot, Workflow, z, actions } from '@botpress/runtime'
|
|
2
|
+
import { ContactsTable } from '../tables/contacts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enrichment pipeline workflow.
|
|
6
|
+
*
|
|
7
|
+
* This scheduled workflow:
|
|
8
|
+
* 1. Fetches contacts that have not been enriched yet.
|
|
9
|
+
* 2. Enriches each contact via the enrichContact action (AI classification).
|
|
10
|
+
* 3. Writes the enrichment results back to the ContactsTable.
|
|
11
|
+
* 4. Updates bot state with run metadata and logs a summary.
|
|
12
|
+
*
|
|
13
|
+
* The workflow uses steps for each phase so progress is checkpointed. If it
|
|
14
|
+
* crashes mid-run, it resumes from the last completed step on restart.
|
|
15
|
+
*
|
|
16
|
+
* Schedule: daily at 9:00 AM UTC (edit the cron expression below).
|
|
17
|
+
* Timeout: 2 hours to handle large contact lists.
|
|
18
|
+
*/
|
|
19
|
+
export const EnrichmentPipeline = new Workflow({
|
|
20
|
+
name: 'enrichmentPipeline',
|
|
21
|
+
description: 'Fetches unenriched CRM contacts, classifies them with AI, and writes results back',
|
|
22
|
+
|
|
23
|
+
// Runs every day at 9:00 AM UTC.
|
|
24
|
+
// Change to "*/30 * * * *" for every 30 minutes during development.
|
|
25
|
+
schedule: '0 9 * * *',
|
|
26
|
+
|
|
27
|
+
// Allow up to 2 hours for large batches.
|
|
28
|
+
timeout: '2h',
|
|
29
|
+
|
|
30
|
+
input: z.object({
|
|
31
|
+
batchSize: z.number().default(50).describe('Max number of contacts to enrich per run'),
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
state: z.object({
|
|
35
|
+
contactsFetched: z.number().default(0),
|
|
36
|
+
contactsEnriched: z.number().default(0),
|
|
37
|
+
errors: z.array(z.string()).default([]),
|
|
38
|
+
}),
|
|
39
|
+
|
|
40
|
+
output: z.object({
|
|
41
|
+
enriched: z.number().describe('Number of contacts enriched this run'),
|
|
42
|
+
errors: z.number().describe('Number of contacts that failed enrichment'),
|
|
43
|
+
summary: z.string().describe('Human-readable run summary'),
|
|
44
|
+
}),
|
|
45
|
+
|
|
46
|
+
async handler({ input, state, step }) {
|
|
47
|
+
// ---------------------------------------------------------------
|
|
48
|
+
// Step 1: Fetch unenriched contacts
|
|
49
|
+
// ---------------------------------------------------------------
|
|
50
|
+
const unenrichedContacts = await step('fetch-unenriched-contacts', async () => {
|
|
51
|
+
const { rows } = await ContactsTable.findRows({
|
|
52
|
+
filter: { enrichedAt: null as unknown as string },
|
|
53
|
+
limit: input.batchSize,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return rows
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
state.contactsFetched = unenrichedContacts.length
|
|
60
|
+
|
|
61
|
+
if (unenrichedContacts.length === 0) {
|
|
62
|
+
console.log('No unenriched contacts found. Skipping run.')
|
|
63
|
+
return {
|
|
64
|
+
enriched: 0,
|
|
65
|
+
errors: 0,
|
|
66
|
+
summary: 'No unenriched contacts to process.',
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(`Found ${unenrichedContacts.length} unenriched contacts. Starting enrichment...`)
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------
|
|
73
|
+
// Step 2: Enrich each contact via the action
|
|
74
|
+
// ---------------------------------------------------------------
|
|
75
|
+
const enrichmentResults = await step('enrich-contacts', async () => {
|
|
76
|
+
const results: Array<{
|
|
77
|
+
id: number
|
|
78
|
+
industry: string
|
|
79
|
+
useCase: string
|
|
80
|
+
score: string
|
|
81
|
+
success: boolean
|
|
82
|
+
error?: string
|
|
83
|
+
}> = []
|
|
84
|
+
|
|
85
|
+
for (const contact of unenrichedContacts) {
|
|
86
|
+
try {
|
|
87
|
+
const enriched = await actions.enrichContact({
|
|
88
|
+
name: contact.name,
|
|
89
|
+
email: contact.email,
|
|
90
|
+
company: contact.company,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
results.push({
|
|
94
|
+
id: contact.id,
|
|
95
|
+
industry: enriched.industry,
|
|
96
|
+
useCase: enriched.useCase,
|
|
97
|
+
score: enriched.score,
|
|
98
|
+
success: true,
|
|
99
|
+
})
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
102
|
+
console.error(`Failed to enrich contact ${contact.id} (${contact.name}): ${message}`)
|
|
103
|
+
results.push({
|
|
104
|
+
id: contact.id,
|
|
105
|
+
industry: '',
|
|
106
|
+
useCase: '',
|
|
107
|
+
score: '',
|
|
108
|
+
success: false,
|
|
109
|
+
error: message,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return results
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------
|
|
118
|
+
// Step 3: Write enrichment results back to the table
|
|
119
|
+
// ---------------------------------------------------------------
|
|
120
|
+
await step('update-contacts', async () => {
|
|
121
|
+
const now = new Date().toISOString()
|
|
122
|
+
const successfulResults = enrichmentResults.filter((r) => r.success)
|
|
123
|
+
|
|
124
|
+
if (successfulResults.length === 0) {
|
|
125
|
+
console.log('No successful enrichments to write back.')
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await ContactsTable.updateRows({
|
|
130
|
+
rows: successfulResults.map((r) => ({
|
|
131
|
+
id: r.id,
|
|
132
|
+
industry: r.industry,
|
|
133
|
+
useCase: r.useCase,
|
|
134
|
+
score: r.score,
|
|
135
|
+
enrichedAt: now,
|
|
136
|
+
})),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
console.log(`Updated ${successfulResults.length} contacts in the table.`)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------
|
|
143
|
+
// Step 4: Update bot state and log summary
|
|
144
|
+
// ---------------------------------------------------------------
|
|
145
|
+
const successCount = enrichmentResults.filter((r) => r.success).length
|
|
146
|
+
const errorCount = enrichmentResults.filter((r) => !r.success).length
|
|
147
|
+
|
|
148
|
+
state.contactsEnriched = successCount
|
|
149
|
+
state.errors = enrichmentResults.filter((r) => !r.success).map((r) => `Contact ${r.id}: ${r.error}`)
|
|
150
|
+
|
|
151
|
+
// Persist metadata in global bot state for cross-run tracking.
|
|
152
|
+
bot.state.lastEnrichmentRunAt = new Date().toISOString()
|
|
153
|
+
bot.state.totalContactsEnriched = (bot.state.totalContactsEnriched ?? 0) + successCount
|
|
154
|
+
|
|
155
|
+
const summary = [
|
|
156
|
+
`Enrichment run complete.`,
|
|
157
|
+
`Processed: ${unenrichedContacts.length}`,
|
|
158
|
+
`Enriched: ${successCount}`,
|
|
159
|
+
`Errors: ${errorCount}`,
|
|
160
|
+
`Total enriched (all-time): ${bot.state.totalContactsEnriched}`,
|
|
161
|
+
].join(' | ')
|
|
162
|
+
|
|
163
|
+
console.log(summary)
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
enriched: successCount,
|
|
167
|
+
errors: errorCount,
|
|
168
|
+
summary,
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": ".",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"paths": {
|
|
17
|
+
"@botpress/runtime/_types/*": ["./.adk/*-types"]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*", ".adk/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A Botpress Agent built with the ADK, pre-configured with chat and webchat integrations.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. Install dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
{{packageManager}} install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Start development server:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
adk dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
3. Chat with your agent locally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
adk chat
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
4. Deploy your agent:
|
|
26
|
+
```bash
|
|
27
|
+
adk deploy
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Project Structure
|
|
31
|
+
|
|
32
|
+
- `src/actions/` - Define callable functions
|
|
33
|
+
- `src/workflows/` - Define long-running processes
|
|
34
|
+
- `src/conversations/` - Define conversation handlers
|
|
35
|
+
- `src/tables/` - Define data storage schemas
|
|
36
|
+
- `src/triggers/` - Define event subscriptions
|
|
37
|
+
- `src/knowledge/` - Add knowledge base files
|
|
38
|
+
|
|
39
|
+
## Integrations
|
|
40
|
+
|
|
41
|
+
This agent comes with **chat** and **webchat** integrations enabled, so you can start talking to it right away with `adk chat`.
|
|
42
|
+
|
|
43
|
+
## Learn More
|
|
44
|
+
|
|
45
|
+
- [ADK Documentation](https://botpress.com/docs/adk)
|
|
46
|
+
- [Botpress Platform](https://botpress.com)
|