@botpress/adk 1.11.8 → 1.11.9
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.map +1 -1
- package/dist/index.js +1274 -15
- package/dist/index.js.map +3 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -654,7 +654,7 @@ var PRETTIER_CONFIG, formatCode = async (code, filepath) => {
|
|
|
654
654
|
`));
|
|
655
655
|
return code;
|
|
656
656
|
}
|
|
657
|
-
}, ADK_VERSION = "1.11.
|
|
657
|
+
}, ADK_VERSION = "1.11.9", relative2 = (from, to) => {
|
|
658
658
|
const fromDir = path10.dirname(from);
|
|
659
659
|
const relative3 = path10.relative(fromDir, to);
|
|
660
660
|
return relative3.startsWith(".") ? relative3 : `./${relative3}`;
|
|
@@ -797,7 +797,7 @@ var init_integration_action_types = __esm(() => {
|
|
|
797
797
|
var require_package = __commonJS((exports, module) => {
|
|
798
798
|
module.exports = {
|
|
799
799
|
name: "@botpress/adk",
|
|
800
|
-
version: "1.11.
|
|
800
|
+
version: "1.11.9",
|
|
801
801
|
description: "Core ADK library for building AI agents on Botpress",
|
|
802
802
|
type: "module",
|
|
803
803
|
main: "dist/index.js",
|
|
@@ -844,7 +844,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
844
844
|
"@botpress/cli": "^4.27.3",
|
|
845
845
|
"@botpress/client": "^1.27.2",
|
|
846
846
|
"@botpress/cognitive": "^0.2.0",
|
|
847
|
-
"@botpress/runtime": "^1.11.
|
|
847
|
+
"@botpress/runtime": "^1.11.9",
|
|
848
848
|
"@botpress/sdk": "^4.18.1",
|
|
849
849
|
"@bpinternal/jex": "^1.2.4",
|
|
850
850
|
"@bpinternal/yargs-extra": "^0.0.21",
|
|
@@ -4827,10 +4827,1271 @@ class ConfigManager {
|
|
|
4827
4827
|
init_utils();
|
|
4828
4828
|
import * as fs11 from "fs";
|
|
4829
4829
|
import * as path15 from "path";
|
|
4830
|
-
import { fileURLToPath } from "url";
|
|
4831
|
-
var __filename2 = fileURLToPath(import.meta.url);
|
|
4832
|
-
var __dirname2 = path15.dirname(__filename2);
|
|
4833
4830
|
|
|
4831
|
+
// src/agent-init/CLAUDE.template.md
|
|
4832
|
+
var CLAUDE_template_default = `# Botpress ADK Project Context
|
|
4833
|
+
|
|
4834
|
+
This project is built with the **Botpress Agent Development Kit (ADK)** - a TypeScript-first framework for building AI agents.
|
|
4835
|
+
|
|
4836
|
+
## Table of Contents
|
|
4837
|
+
|
|
4838
|
+
- [Quick Reference: Use the Botpress MCP Server](#quick-reference-use-the-botpress-mcp-server)
|
|
4839
|
+
- [What is the ADK?](#what-is-the-adk)
|
|
4840
|
+
- [ADK CLI](#adk-cli)
|
|
4841
|
+
- [Core Concepts](#core-concepts)
|
|
4842
|
+
- [1. Agent Configuration](#1-agent-configuration-agentconfigts)
|
|
4843
|
+
- [2. Conversations](#2-conversations-srcconversations)
|
|
4844
|
+
- [3. Workflows](#3-workflows-srcworkflows)
|
|
4845
|
+
- [4. Tools](#4-tools-srctools)
|
|
4846
|
+
- [5. Knowledge Bases](#5-knowledge-bases-srcknowledge)
|
|
4847
|
+
- [6. Actions](#6-actions-srcactions)
|
|
4848
|
+
- [7. Zai Library](#7-zai-library)
|
|
4849
|
+
- [Project Structure](#project-structure)
|
|
4850
|
+
- [Development Workflow](#development-workflow)
|
|
4851
|
+
- [Examples](#examples)
|
|
4852
|
+
- [Best Practices](#best-practices)
|
|
4853
|
+
- [Common APIs](#common-apis)
|
|
4854
|
+
- [Advanced Autonomous Execution](#advanced-autonomous-execution)
|
|
4855
|
+
- [State and Metadata Management](#state-and-metadata-management)
|
|
4856
|
+
- [Advanced Table Operations](#advanced-table-operations)
|
|
4857
|
+
- [Knowledge Base Operations](#knowledge-base-operations)
|
|
4858
|
+
- [Advanced Conversation Patterns](#advanced-conversation-patterns)
|
|
4859
|
+
- [Citations System](#citations-system)
|
|
4860
|
+
- [When Making Changes](#when-making-changes)
|
|
4861
|
+
- [Resources](#resources)
|
|
4862
|
+
|
|
4863
|
+
## Quick Reference: Use the Botpress MCP Server
|
|
4864
|
+
|
|
4865
|
+
**IMPORTANT**: When working on this project, always search the Botpress documentation using the \`mcp__botpress-docs__SearchBotpress\` tool before making changes. The ADK has specific patterns and APIs that are well-documented.
|
|
4866
|
+
|
|
4867
|
+
## What is the ADK?
|
|
4868
|
+
|
|
4869
|
+
The ADK allows developers to build Botpress agents using **code instead of the Studio interface**. It provides:
|
|
4870
|
+
|
|
4871
|
+
- Project scaffolding with TypeScript
|
|
4872
|
+
- Hot reloading development server (\`adk dev\`)
|
|
4873
|
+
- Type-safe APIs and auto-generated types
|
|
4874
|
+
- Build and deploy to Botpress Cloud
|
|
4875
|
+
|
|
4876
|
+
## ADK CLI
|
|
4877
|
+
|
|
4878
|
+
The ADK CLI is installed globally. You can run it using \`adk <command>\`.
|
|
4879
|
+
Always use bash to run ADK. (\`Bash(adk)\`)
|
|
4880
|
+
To install an integration: \`adk install <integration>\`
|
|
4881
|
+
To generate types without running in dev mode: \`adk build\`
|
|
4882
|
+
|
|
4883
|
+
## Core Concepts
|
|
4884
|
+
|
|
4885
|
+
### 1. Agent Configuration (\`agent.config.ts\`)
|
|
4886
|
+
|
|
4887
|
+
The main configuration file defines:
|
|
4888
|
+
|
|
4889
|
+
- **Agent name and description**
|
|
4890
|
+
- **Default models** for autonomous and zai operations
|
|
4891
|
+
- **State schemas** (bot-level and user-level state using Zod)
|
|
4892
|
+
- **Configuration variables** (encrypted, secure storage for API keys)
|
|
4893
|
+
- **Integration dependencies** (webchat, chat, etc.)
|
|
4894
|
+
|
|
4895
|
+
\`\`\`typescript
|
|
4896
|
+
export default defineConfig({
|
|
4897
|
+
name: "my-agent",
|
|
4898
|
+
defaultModels: {
|
|
4899
|
+
autonomous: "cerebras:gpt-oss-120b",
|
|
4900
|
+
zai: "cerebras:gpt-oss-120b",
|
|
4901
|
+
},
|
|
4902
|
+
bot: { state: z.object({}) },
|
|
4903
|
+
user: { state: z.object({}) },
|
|
4904
|
+
dependencies: {
|
|
4905
|
+
integrations: {
|
|
4906
|
+
webchat: { version: "webchat@0.3.0", enabled: true },
|
|
4907
|
+
},
|
|
4908
|
+
},
|
|
4909
|
+
});
|
|
4910
|
+
\`\`\`
|
|
4911
|
+
|
|
4912
|
+
### 2. Conversations (\`src/conversations/\`)
|
|
4913
|
+
|
|
4914
|
+
**Primary way agents handle user messages**. Each conversation handler:
|
|
4915
|
+
|
|
4916
|
+
- Responds to messages from specific channels
|
|
4917
|
+
- Uses \`execute()\` to run autonomous AI logic
|
|
4918
|
+
- Can access conversation state, send messages, and call tools
|
|
4919
|
+
|
|
4920
|
+
**Key Pattern**: The \`execute()\` function runs the agent's AI loop:
|
|
4921
|
+
|
|
4922
|
+
\`\`\`typescript
|
|
4923
|
+
export default new Conversation({
|
|
4924
|
+
channel: "webchat.channel",
|
|
4925
|
+
handler: async ({ execute, conversation, state }) => {
|
|
4926
|
+
await execute({
|
|
4927
|
+
instructions: "Your agent's instructions here",
|
|
4928
|
+
tools: [myTool1, myTool2],
|
|
4929
|
+
knowledge: [myKnowledgeBase],
|
|
4930
|
+
});
|
|
4931
|
+
},
|
|
4932
|
+
});
|
|
4933
|
+
\`\`\`
|
|
4934
|
+
|
|
4935
|
+
### 3. Workflows (\`src/workflows/\`)
|
|
4936
|
+
|
|
4937
|
+
**Long-running processes** for complex, multi-step operations:
|
|
4938
|
+
|
|
4939
|
+
- Can run on schedules (cron syntax)
|
|
4940
|
+
- Run independently or triggered by events
|
|
4941
|
+
- NOT the same as Studio Workflows
|
|
4942
|
+
- Use \`step()\` for durable execution (survives restarts)
|
|
4943
|
+
|
|
4944
|
+
\`\`\`typescript
|
|
4945
|
+
export default new Workflow({
|
|
4946
|
+
name: "periodic-indexing",
|
|
4947
|
+
schedule: "0 */6 * * *",
|
|
4948
|
+
handler: async ({ step }) => {
|
|
4949
|
+
await step("task-name", async () => {
|
|
4950
|
+
// Your logic here
|
|
4951
|
+
});
|
|
4952
|
+
},
|
|
4953
|
+
});
|
|
4954
|
+
\`\`\`
|
|
4955
|
+
|
|
4956
|
+
#### Advanced Workflow Step Methods
|
|
4957
|
+
|
|
4958
|
+
Beyond basic \`step()\`, workflows have powerful methods for complex orchestration:
|
|
4959
|
+
|
|
4960
|
+
**Parallel Processing:**
|
|
4961
|
+
|
|
4962
|
+
- \`step.map()\` - Process array items in parallel with concurrency control
|
|
4963
|
+
- \`step.forEach()\` - Like map but for side effects (returns void)
|
|
4964
|
+
- \`step.batch()\` - Process in sequential batches
|
|
4965
|
+
|
|
4966
|
+
\`\`\`typescript
|
|
4967
|
+
// Process items in parallel
|
|
4968
|
+
const results = await step.map(
|
|
4969
|
+
'process-items',
|
|
4970
|
+
items,
|
|
4971
|
+
async (item, { i }) => processItem(item),
|
|
4972
|
+
{ concurrency: 5, maxAttempts: 3 }
|
|
4973
|
+
)
|
|
4974
|
+
|
|
4975
|
+
// Batch processing
|
|
4976
|
+
await step.batch(
|
|
4977
|
+
'bulk-insert',
|
|
4978
|
+
records,
|
|
4979
|
+
async (batch) => database.bulkInsert(batch),
|
|
4980
|
+
{ batchSize: 100 }
|
|
4981
|
+
)
|
|
4982
|
+
\`\`\`
|
|
4983
|
+
|
|
4984
|
+
**Workflow Coordination:**
|
|
4985
|
+
|
|
4986
|
+
- \`step.waitForWorkflow()\` - Wait for another workflow to complete
|
|
4987
|
+
- \`step.executeWorkflow()\` - Start and wait in one call
|
|
4988
|
+
|
|
4989
|
+
\`\`\`typescript
|
|
4990
|
+
const result = await step.executeWorkflow('run-child', ChildWorkflow, { input })
|
|
4991
|
+
\`\`\`
|
|
4992
|
+
|
|
4993
|
+
**Timing Control:**
|
|
4994
|
+
|
|
4995
|
+
- \`step.sleep()\` - Pause execution (< 10s in-memory, >= 10s uses listening mode)
|
|
4996
|
+
- \`step.sleepUntil()\` - Sleep until specific time
|
|
4997
|
+
- \`step.listen()\` - Pause and wait for external event
|
|
4998
|
+
|
|
4999
|
+
\`\`\`typescript
|
|
5000
|
+
await step.sleep('wait-5s', 5000)
|
|
5001
|
+
await step.sleepUntil('wait-until-noon', new Date('2025-01-15T12:00:00Z'))
|
|
5002
|
+
\`\`\`
|
|
5003
|
+
|
|
5004
|
+
**Request Data from Conversation:**
|
|
5005
|
+
|
|
5006
|
+
\`\`\`typescript
|
|
5007
|
+
// In workflow
|
|
5008
|
+
const { topic } = await step.request('topic', 'What topic should I research?')
|
|
5009
|
+
|
|
5010
|
+
// In conversation
|
|
5011
|
+
if (isWorkflowDataRequest(event)) {
|
|
5012
|
+
await workflow.provide(event, { topic: userInput })
|
|
5013
|
+
}
|
|
5014
|
+
\`\`\`
|
|
5015
|
+
|
|
5016
|
+
**Execution Control:**
|
|
5017
|
+
|
|
5018
|
+
- \`step.fail()\` - Mark workflow as failed
|
|
5019
|
+
- \`step.abort()\` - Abort without failing
|
|
5020
|
+
- \`step.progress()\` - Record progress checkpoint
|
|
5021
|
+
|
|
5022
|
+
### 4. Tools (\`src/tools/\`)
|
|
5023
|
+
|
|
5024
|
+
**AI-callable functions** that enable agents to perform actions:
|
|
5025
|
+
|
|
5026
|
+
- Must have clear name and description
|
|
5027
|
+
- Use Zod schemas for input/output
|
|
5028
|
+
- Can be passed to \`execute()\`
|
|
5029
|
+
|
|
5030
|
+
\`\`\`typescript
|
|
5031
|
+
export default new Autonomous.Tool({
|
|
5032
|
+
name: "searchDatabase",
|
|
5033
|
+
description: "Search the database",
|
|
5034
|
+
input: z.object({ query: z.string() }),
|
|
5035
|
+
output: z.object({ results: z.array(z.any()) }),
|
|
5036
|
+
handler: async ({ query }) => {
|
|
5037
|
+
// Tool logic
|
|
5038
|
+
return { results: [] };
|
|
5039
|
+
},
|
|
5040
|
+
});
|
|
5041
|
+
\`\`\`
|
|
5042
|
+
|
|
5043
|
+
### 5. Knowledge Bases (\`src/knowledge/\`)
|
|
5044
|
+
|
|
5045
|
+
**RAG (Retrieval-Augmented Generation)** for providing context:
|
|
5046
|
+
|
|
5047
|
+
- Website scraping
|
|
5048
|
+
- Document ingestion
|
|
5049
|
+
- Can be passed to \`execute()\` via \`knowledge\` parameter
|
|
5050
|
+
|
|
5051
|
+
### 6. Actions (\`src/actions/\`)
|
|
5052
|
+
|
|
5053
|
+
**Reusable business logic** that can:
|
|
5054
|
+
|
|
5055
|
+
- Be called from anywhere (import \`actions\` from \`@botpress/runtime\`)
|
|
5056
|
+
- Be converted to tools with \`.asTool()\`
|
|
5057
|
+
- Encapsulate logic not tied to conversational flow
|
|
5058
|
+
|
|
5059
|
+
### 7. Zai Library
|
|
5060
|
+
|
|
5061
|
+
**Zai** is an LLM utility library that provides a clean, type-safe API for common AI operations. It's designed to work seamlessly with the ADK and SDK to process LLM inputs and outputs programmatically.
|
|
5062
|
+
|
|
5063
|
+
#### Importing Zai in ADK
|
|
5064
|
+
|
|
5065
|
+
In the ADK, Zai is available from \`@botpress/runtime\`:
|
|
5066
|
+
|
|
5067
|
+
\`\`\`typescript
|
|
5068
|
+
import { adk } from '@botpress/runtime'
|
|
5069
|
+
// then adk.zai.<method_name>
|
|
5070
|
+
\`\`\`
|
|
5071
|
+
|
|
5072
|
+
The default model for Zai operations is configured in \`agent.config.ts\`:
|
|
5073
|
+
|
|
5074
|
+
\`\`\`typescript
|
|
5075
|
+
export default defineConfig({
|
|
5076
|
+
defaultModels: {
|
|
5077
|
+
autonomous: "cerebras:gpt-oss-120b",
|
|
5078
|
+
zai: "cerebras:gpt-oss-120b", // Model used for Zai operations
|
|
5079
|
+
},
|
|
5080
|
+
})
|
|
5081
|
+
\`\`\`
|
|
5082
|
+
|
|
5083
|
+
#### When to Use Zai
|
|
5084
|
+
|
|
5085
|
+
Use Zai when you need to:
|
|
5086
|
+
- Extract structured data from unstructured text
|
|
5087
|
+
- Answer questions from documents with source citations
|
|
5088
|
+
- Verify Boolean conditions in content
|
|
5089
|
+
- Summarize long text into concise summaries
|
|
5090
|
+
- Generate text programmatically based on prompts
|
|
5091
|
+
|
|
5092
|
+
**Use Zai instead of \`execute()\` when**: You need deterministic, structured outputs for specific AI tasks (extraction, validation, summarization) rather than conversational interactions.
|
|
5093
|
+
|
|
5094
|
+
#### Zai Methods
|
|
5095
|
+
|
|
5096
|
+
**1. \`answer()\` - Answer Questions with Citations**
|
|
5097
|
+
|
|
5098
|
+
Answers questions from documents with intelligent source citations.
|
|
5099
|
+
|
|
5100
|
+
\`\`\`typescript
|
|
5101
|
+
const documents = [
|
|
5102
|
+
'Botpress was founded in 2016.',
|
|
5103
|
+
'The company is based in Quebec, Canada.',
|
|
5104
|
+
]
|
|
5105
|
+
|
|
5106
|
+
const result = await zai.answer(documents, 'When was Botpress founded?')
|
|
5107
|
+
|
|
5108
|
+
if (result.type === 'answer') {
|
|
5109
|
+
console.log(result.answer) // "Botpress was founded in 2016."
|
|
5110
|
+
console.log(result.citations) // Array of citations with source references
|
|
5111
|
+
}
|
|
5112
|
+
\`\`\`
|
|
5113
|
+
|
|
5114
|
+
**When to use**: When you need to answer questions from a set of documents with traceable sources (e.g., custom RAG implementations, document Q&A).
|
|
5115
|
+
|
|
5116
|
+
**2. \`extract()\` - Extract Structured Data**
|
|
5117
|
+
|
|
5118
|
+
Extracts structured data from unstructured input using Zod schemas.
|
|
5119
|
+
|
|
5120
|
+
\`\`\`typescript
|
|
5121
|
+
import { z, adk } from '@botpress/runtime'
|
|
5122
|
+
|
|
5123
|
+
const userSchema = z.object({
|
|
5124
|
+
name: z.string(),
|
|
5125
|
+
email: z.string().email(),
|
|
5126
|
+
age: z.number()
|
|
5127
|
+
})
|
|
5128
|
+
|
|
5129
|
+
const input = "My name is John Doe, I'm 30 years old and my email is john@example.com"
|
|
5130
|
+
// zai.extract returns the extracted data DIRECTLY (not wrapped in { output: ... })
|
|
5131
|
+
const result = await adk.zai.extract(input, userSchema)
|
|
5132
|
+
|
|
5133
|
+
console.log(result)
|
|
5134
|
+
// { name: "John Doe", email: "john@example.com", age: 30 }
|
|
5135
|
+
\`\`\`
|
|
5136
|
+
|
|
5137
|
+
**When to use**: When you need to parse unstructured user input into structured data (e.g., form extraction from natural language, parsing contact information).
|
|
5138
|
+
|
|
5139
|
+
**3. \`check()\` - Verify Boolean Conditions**
|
|
5140
|
+
|
|
5141
|
+
Verifies a condition against some input and returns a boolean with explanation.
|
|
5142
|
+
|
|
5143
|
+
\`\`\`typescript
|
|
5144
|
+
const email = "Get rich quick! Click here now!!!"
|
|
5145
|
+
const { output } = await zai.check(email, 'is spam').result()
|
|
5146
|
+
|
|
5147
|
+
console.log(output.value) // true
|
|
5148
|
+
console.log(output.explanation) // "This email contains typical spam indicators..."
|
|
5149
|
+
\`\`\`
|
|
5150
|
+
|
|
5151
|
+
**When to use**: When you need to validate content or make binary decisions (e.g., content moderation, intent verification, condition checking).
|
|
5152
|
+
|
|
5153
|
+
**4. \`summarize()\` - Summarize Text**
|
|
5154
|
+
|
|
5155
|
+
Creates concise summaries of lengthy text to a desired length.
|
|
5156
|
+
|
|
5157
|
+
\`\`\`typescript
|
|
5158
|
+
const longArticle = "..." // Long article content
|
|
5159
|
+
|
|
5160
|
+
const summary = await zai.summarize(longArticle, {
|
|
5161
|
+
length: 100, // tokens
|
|
5162
|
+
prompt: 'key findings and main conclusions'
|
|
5163
|
+
})
|
|
5164
|
+
\`\`\`
|
|
5165
|
+
|
|
5166
|
+
**When to use**: When you need to condense long content (e.g., article summaries, transcript summaries, document overviews).
|
|
5167
|
+
|
|
5168
|
+
**5. \`text()\` - Generate Text**
|
|
5169
|
+
|
|
5170
|
+
Generates text of the desired length according to a prompt.
|
|
5171
|
+
|
|
5172
|
+
\`\`\`typescript
|
|
5173
|
+
const generated = await zai.text('Write a welcome message for new users', {
|
|
5174
|
+
length: 50 // tokens
|
|
5175
|
+
})
|
|
5176
|
+
\`\`\`
|
|
5177
|
+
|
|
5178
|
+
**When to use**: When you need to generate specific text content programmatically (e.g., dynamic content generation, templated responses).
|
|
5179
|
+
|
|
5180
|
+
#### Response Methods
|
|
5181
|
+
|
|
5182
|
+
All Zai operations return a Response object with promise-like behavior and additional functionality:
|
|
5183
|
+
|
|
5184
|
+
\`\`\`typescript
|
|
5185
|
+
// Await the result directly
|
|
5186
|
+
const result = await zai.extract(input, schema)
|
|
5187
|
+
|
|
5188
|
+
// Or use .result() for explicit promise handling
|
|
5189
|
+
const { output } = await zai.check(content, 'is valid').result()
|
|
5190
|
+
\`\`\`
|
|
5191
|
+
|
|
5192
|
+
## Project Structure
|
|
5193
|
+
|
|
5194
|
+
\`\`\`
|
|
5195
|
+
agent.config.ts # Main configuration
|
|
5196
|
+
src/
|
|
5197
|
+
conversations/ # Message handlers (primary user interaction)
|
|
5198
|
+
workflows/ # Long-running processes
|
|
5199
|
+
tools/ # AI-callable functions
|
|
5200
|
+
actions/ # Reusable business logic
|
|
5201
|
+
knowledge/ # Knowledge bases for RAG
|
|
5202
|
+
triggers/ # Event-based triggers
|
|
5203
|
+
tables/ # Database tables
|
|
5204
|
+
.botpress/ # Auto-generated types (DO NOT EDIT)
|
|
5205
|
+
\`\`\`
|
|
5206
|
+
|
|
5207
|
+
## Development Workflow
|
|
5208
|
+
|
|
5209
|
+
1. **Start dev server**: \`adk dev\` (http://localhost:3001 for console)
|
|
5210
|
+
2. **Add integrations**: \`adk add webchat@latest\`
|
|
5211
|
+
3. **Build**: \`adk build\`
|
|
5212
|
+
4. **Deploy**: \`adk deploy\`
|
|
5213
|
+
5. **Chat in CLI**: \`adk chat\`
|
|
5214
|
+
|
|
5215
|
+
## Examples
|
|
5216
|
+
|
|
5217
|
+
Official examples: https://github.com/botpress/adk/tree/main/examples
|
|
5218
|
+
|
|
5219
|
+
### subagents
|
|
5220
|
+
|
|
5221
|
+
**What you'll learn:** How to build a multi-agent system where an orchestrator delegates to specialists.
|
|
5222
|
+
|
|
5223
|
+
Shows the \`SubAgent\` pattern where each specialist (HR, IT, Sales, etc.) runs in its own context with \`mode: "worker"\`, returns structured results via custom exits, and reports progress through \`onTrace\` hooks.
|
|
5224
|
+
|
|
5225
|
+
### webchat-rag
|
|
5226
|
+
|
|
5227
|
+
**What you'll learn:** How to build a RAG assistant with scheduled indexing, guardrails, and admin features.
|
|
5228
|
+
|
|
5229
|
+
Shows \`Autonomous.Object\` for dynamic tool grouping, \`onBeforeTool\` hooks to enforce knowledge search before answering, scheduled workflows for KB refresh, and \`ThinkSignal\` for interrupting execution.
|
|
5230
|
+
|
|
5231
|
+
### deep-research
|
|
5232
|
+
|
|
5233
|
+
**What you'll learn:** How to build complex, long-running workflows with progress tracking.
|
|
5234
|
+
|
|
5235
|
+
Shows \`step()\` and \`step.map()\` for workflow phases, \`Reference.Workflow\` for conversation-workflow linking, Tables for activity tracking, and extensive Zai usage (\`extract\`, \`answer\`, \`filter\`, \`text\`).
|
|
5236
|
+
|
|
5237
|
+
## Best Practices
|
|
5238
|
+
|
|
5239
|
+
1. **Search Botpress docs first** - Use the MCP tool before implementing
|
|
5240
|
+
2. **Keep tools focused** - Single responsibility per tool
|
|
5241
|
+
3. **Use Zod schemas** with \`.describe()\` for clarity
|
|
5242
|
+
4. **State management** - Minimize large variables in main workflow
|
|
5243
|
+
5. **Type safety** - Run \`adk dev\` or \`adk build\` to regenerate types after config changes
|
|
5244
|
+
6. **Conversations vs Workflows**:
|
|
5245
|
+
- Conversations: User interactions, real-time responses
|
|
5246
|
+
- Workflows: Background tasks, scheduled jobs, long-running processes
|
|
5247
|
+
|
|
5248
|
+
## Common APIs
|
|
5249
|
+
|
|
5250
|
+
### Conversation Handler
|
|
5251
|
+
|
|
5252
|
+
\`\`\`typescript
|
|
5253
|
+
handler: async ({
|
|
5254
|
+
execute, // Run autonomous AI loop
|
|
5255
|
+
conversation, // Send messages, manage conversation
|
|
5256
|
+
state, // Conversation state (persisted)
|
|
5257
|
+
message, // Incoming message
|
|
5258
|
+
client, // Botpress API client
|
|
5259
|
+
}) => {};
|
|
5260
|
+
\`\`\`
|
|
5261
|
+
|
|
5262
|
+
### Execute Function
|
|
5263
|
+
|
|
5264
|
+
\`\`\`typescript
|
|
5265
|
+
await execute({
|
|
5266
|
+
instructions: "String or function returning instructions",
|
|
5267
|
+
tools: [tool1, tool2], // Optional tools
|
|
5268
|
+
knowledge: [kb1, kb2], // Optional knowledge bases
|
|
5269
|
+
exits: [customExit], // Optional custom exits
|
|
5270
|
+
hooks: { onTrace, onBeforeTool }, // Optional hooks
|
|
5271
|
+
mode: "worker", // Optional: autonomous until exit
|
|
5272
|
+
iterations: 10, // Max loops (default 10)
|
|
5273
|
+
});
|
|
5274
|
+
\`\`\`
|
|
5275
|
+
|
|
5276
|
+
## Advanced Autonomous Execution
|
|
5277
|
+
|
|
5278
|
+
### Autonomous Namespace
|
|
5279
|
+
|
|
5280
|
+
The \`Autonomous\` namespace provides powerful primitives for controlling LLM behavior:
|
|
5281
|
+
|
|
5282
|
+
#### Autonomous.Exit - Custom Exit Conditions
|
|
5283
|
+
|
|
5284
|
+
Define custom exits for autonomous execution loops:
|
|
5285
|
+
|
|
5286
|
+
\`\`\`typescript
|
|
5287
|
+
import { Autonomous, z } from '@botpress/runtime'
|
|
5288
|
+
|
|
5289
|
+
const AnswerExit = new Autonomous.Exit({
|
|
5290
|
+
name: 'answer',
|
|
5291
|
+
description: 'Return when you have the final answer',
|
|
5292
|
+
schema: z.object({
|
|
5293
|
+
answer: z.string(),
|
|
5294
|
+
confidence: z.number()
|
|
5295
|
+
})
|
|
5296
|
+
})
|
|
5297
|
+
|
|
5298
|
+
const NoAnswerExit = new Autonomous.Exit({
|
|
5299
|
+
name: 'no_answer',
|
|
5300
|
+
description: 'No answer could be found'
|
|
5301
|
+
})
|
|
5302
|
+
|
|
5303
|
+
const result = await execute({
|
|
5304
|
+
instructions: 'Research and answer the question',
|
|
5305
|
+
exits: [AnswerExit, NoAnswerExit],
|
|
5306
|
+
mode: 'worker' // Run until exit triggered
|
|
5307
|
+
})
|
|
5308
|
+
|
|
5309
|
+
// ✅ CORRECT - Use result.is() and result.output
|
|
5310
|
+
if (result.is(AnswerExit)) {
|
|
5311
|
+
console.log(result.output.answer) // Type-safe access
|
|
5312
|
+
console.log(result.output.confidence)
|
|
5313
|
+
} else if (result.is(NoAnswerExit)) {
|
|
5314
|
+
console.log('No answer found')
|
|
5315
|
+
}
|
|
5316
|
+
|
|
5317
|
+
// ❌ WRONG - Don't use result.exit.name or result.exit.value
|
|
5318
|
+
// if (result.exit?.name === 'answer') { ... }
|
|
5319
|
+
\`\`\`
|
|
5320
|
+
|
|
5321
|
+
#### Autonomous.ThinkSignal - Inject Context
|
|
5322
|
+
|
|
5323
|
+
Provide context to the LLM without continuing execution:
|
|
5324
|
+
|
|
5325
|
+
\`\`\`typescript
|
|
5326
|
+
const results = await fetchData()
|
|
5327
|
+
|
|
5328
|
+
if (!results.length) {
|
|
5329
|
+
throw new ThinkSignal('error', 'No results found')
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5332
|
+
// Inject formatted results into LLM context
|
|
5333
|
+
throw new ThinkSignal('results ready', formatResults(results))
|
|
5334
|
+
\`\`\`
|
|
5335
|
+
|
|
5336
|
+
#### Autonomous.Object - Dynamic Tool Grouping
|
|
5337
|
+
|
|
5338
|
+
Group tools dynamically based on state:
|
|
5339
|
+
|
|
5340
|
+
\`\`\`typescript
|
|
5341
|
+
const adminTools = new Autonomous.Object({
|
|
5342
|
+
name: 'admin',
|
|
5343
|
+
description: user.isAdmin ? 'Admin tools available' : 'Login required',
|
|
5344
|
+
tools: user.isAdmin ? [refreshKB, manageBots] : [generateLoginCode]
|
|
5345
|
+
})
|
|
5346
|
+
|
|
5347
|
+
await execute({
|
|
5348
|
+
objects: [adminTools]
|
|
5349
|
+
})
|
|
5350
|
+
\`\`\`
|
|
5351
|
+
|
|
5352
|
+
### Execution Hooks
|
|
5353
|
+
|
|
5354
|
+
Full control over the autonomous execution loop:
|
|
5355
|
+
|
|
5356
|
+
\`\`\`typescript
|
|
5357
|
+
await execute({
|
|
5358
|
+
instructions: '...',
|
|
5359
|
+
hooks: {
|
|
5360
|
+
// Before tool execution - can modify input
|
|
5361
|
+
onBeforeTool: async ({ iteration, tool, input, controller }) => {
|
|
5362
|
+
console.log(\`About to call \${tool.name}\`)
|
|
5363
|
+
return { input: modifiedInput } // Optional: transform input
|
|
5364
|
+
},
|
|
5365
|
+
|
|
5366
|
+
// After tool execution - can modify output
|
|
5367
|
+
onAfterTool: async ({ iteration, tool, input, output, controller }) => {
|
|
5368
|
+
console.log(\`\${tool.name} returned:\`, output)
|
|
5369
|
+
return { output: modifiedOutput } // Optional: transform output
|
|
5370
|
+
},
|
|
5371
|
+
|
|
5372
|
+
// Before code execution in iteration
|
|
5373
|
+
onBeforeExecution: async (iteration, controller) => {
|
|
5374
|
+
return { code: modifiedCode } // Optional: transform generated code
|
|
5375
|
+
},
|
|
5376
|
+
|
|
5377
|
+
// When exit is triggered
|
|
5378
|
+
onExit: async (result) => {
|
|
5379
|
+
console.log('Exited with:', result)
|
|
5380
|
+
},
|
|
5381
|
+
|
|
5382
|
+
// After each iteration completes
|
|
5383
|
+
onIterationEnd: async (iteration, controller) => {
|
|
5384
|
+
if (iteration > 5) {
|
|
5385
|
+
controller.abort() // Stop execution
|
|
5386
|
+
}
|
|
5387
|
+
},
|
|
5388
|
+
|
|
5389
|
+
// On trace events (synchronous, non-blocking)
|
|
5390
|
+
onTrace: ({ trace, iteration }) => {
|
|
5391
|
+
if (trace.type === 'comment') {
|
|
5392
|
+
console.log('LLM thinking:', trace.comment)
|
|
5393
|
+
}
|
|
5394
|
+
if (trace.type === 'tool_call') {
|
|
5395
|
+
console.log('Calling:', trace.tool_name)
|
|
5396
|
+
}
|
|
5397
|
+
}
|
|
5398
|
+
}
|
|
5399
|
+
})
|
|
5400
|
+
\`\`\`
|
|
5401
|
+
|
|
5402
|
+
**Hook use cases:**
|
|
5403
|
+
- Logging and debugging
|
|
5404
|
+
- Input/output validation and transformation
|
|
5405
|
+
- Rate limiting tool calls
|
|
5406
|
+
- Custom abort conditions
|
|
5407
|
+
- Injecting dynamic context
|
|
5408
|
+
|
|
5409
|
+
## State and Metadata Management
|
|
5410
|
+
|
|
5411
|
+
### Tags - Key-Value Metadata
|
|
5412
|
+
|
|
5413
|
+
Track metadata for any entity (bot, user, conversation, workflow):
|
|
5414
|
+
|
|
5415
|
+
\`\`\`typescript
|
|
5416
|
+
import { TrackedTags } from '@botpress/runtime'
|
|
5417
|
+
|
|
5418
|
+
// Create tags instance
|
|
5419
|
+
const tags = TrackedTags.create({
|
|
5420
|
+
type: 'bot', // or 'user' | 'conversation' | 'workflow'
|
|
5421
|
+
id: entityId,
|
|
5422
|
+
client: botClient,
|
|
5423
|
+
initialTags: { status: 'active' }
|
|
5424
|
+
})
|
|
5425
|
+
|
|
5426
|
+
// Load from server
|
|
5427
|
+
await tags.load()
|
|
5428
|
+
|
|
5429
|
+
// Modify tags
|
|
5430
|
+
tags.tags = {
|
|
5431
|
+
...tags.tags,
|
|
5432
|
+
lastSync: new Date().toISOString()
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5435
|
+
// Check if modified
|
|
5436
|
+
if (tags.isDirty()) {
|
|
5437
|
+
await tags.save()
|
|
5438
|
+
}
|
|
5439
|
+
|
|
5440
|
+
// Batch operations
|
|
5441
|
+
await TrackedTags.saveAllDirty()
|
|
5442
|
+
await TrackedTags.loadAll()
|
|
5443
|
+
\`\`\`
|
|
5444
|
+
|
|
5445
|
+
**Access via workflow instance:**
|
|
5446
|
+
|
|
5447
|
+
\`\`\`typescript
|
|
5448
|
+
workflow.tags = { status: 'processing' }
|
|
5449
|
+
await workflow.save()
|
|
5450
|
+
\`\`\`
|
|
5451
|
+
|
|
5452
|
+
### Reference.Workflow - Typed Workflow References
|
|
5453
|
+
|
|
5454
|
+
Serialize workflow references in state that auto-hydrate on access:
|
|
5455
|
+
|
|
5456
|
+
\`\`\`typescript
|
|
5457
|
+
import { Reference, z } from '@botpress/runtime'
|
|
5458
|
+
|
|
5459
|
+
// In conversation state schema
|
|
5460
|
+
state: z.object({
|
|
5461
|
+
research: Reference.Workflow('deep_research').optional()
|
|
5462
|
+
// or untyped: Reference.Workflow().optional()
|
|
5463
|
+
})
|
|
5464
|
+
|
|
5465
|
+
// In handler - always a WorkflowInstance
|
|
5466
|
+
handler: async ({ state }) => {
|
|
5467
|
+
if (state.research) {
|
|
5468
|
+
// state.research is typed WorkflowInstance
|
|
5469
|
+
console.log(state.research.status) // 'running' | 'completed' | etc
|
|
5470
|
+
console.log(state.research.output) // Typed output
|
|
5471
|
+
|
|
5472
|
+
if (state.research.status === 'completed') {
|
|
5473
|
+
// Access completed workflow data
|
|
5474
|
+
}
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
\`\`\`
|
|
5478
|
+
|
|
5479
|
+
### Context Object - Runtime Access
|
|
5480
|
+
|
|
5481
|
+
Global context for accessing runtime information:
|
|
5482
|
+
|
|
5483
|
+
\`\`\`typescript
|
|
5484
|
+
import { context } from '@botpress/runtime'
|
|
5485
|
+
|
|
5486
|
+
// Get specific context
|
|
5487
|
+
const client = context.get('client')
|
|
5488
|
+
const citations = context.get('citations')
|
|
5489
|
+
const logger = context.get('logger')
|
|
5490
|
+
|
|
5491
|
+
// Get all context
|
|
5492
|
+
const { client, cognitive, logger, operation } = context.getAll()
|
|
5493
|
+
\`\`\`
|
|
5494
|
+
|
|
5495
|
+
**Available context properties:**
|
|
5496
|
+
- \`client\` - Botpress API client
|
|
5497
|
+
- \`cognitive\` - LLM access
|
|
5498
|
+
- \`logger\` - Logging
|
|
5499
|
+
- \`operation\` - Current operation info
|
|
5500
|
+
- \`citations\` - Citation tracking
|
|
5501
|
+
- \`chat\` - Chat interface
|
|
5502
|
+
- \`bot\` - Bot tags and metadata
|
|
5503
|
+
- \`user\` - User information
|
|
5504
|
+
- \`conversation\` - Current conversation
|
|
5505
|
+
- \`message\` - Incoming message
|
|
5506
|
+
- \`event\` - Current event
|
|
5507
|
+
- \`workflow\` - Current workflow
|
|
5508
|
+
- \`workflowControlContext\` - Workflow control (abort, fail, restart)
|
|
5509
|
+
|
|
5510
|
+
### State Management
|
|
5511
|
+
|
|
5512
|
+
Access and modify tracked state:
|
|
5513
|
+
|
|
5514
|
+
\`\`\`typescript
|
|
5515
|
+
import { bot, user } from '@botpress/runtime'
|
|
5516
|
+
|
|
5517
|
+
// Bot state
|
|
5518
|
+
bot.state.lastIndexed = new Date().toISOString()
|
|
5519
|
+
bot.state.config = { theme: 'dark' }
|
|
5520
|
+
|
|
5521
|
+
// User state
|
|
5522
|
+
user.state.preferences = { notifications: true }
|
|
5523
|
+
user.state.lastActive = Date.now()
|
|
5524
|
+
\`\`\`
|
|
5525
|
+
|
|
5526
|
+
State persists automatically across executions.
|
|
5527
|
+
|
|
5528
|
+
## Advanced Table Operations
|
|
5529
|
+
|
|
5530
|
+
### Table Naming Rules
|
|
5531
|
+
|
|
5532
|
+
**IMPORTANT**: Tables have strict naming requirements:
|
|
5533
|
+
|
|
5534
|
+
\`\`\`typescript
|
|
5535
|
+
// ✅ CORRECT - Name must end with "Table"
|
|
5536
|
+
export const MyDataTable = new Table({
|
|
5537
|
+
name: "mydataTable", // Must end with "Table"
|
|
5538
|
+
columns: { ... }
|
|
5539
|
+
});
|
|
5540
|
+
|
|
5541
|
+
// ❌ WRONG - Missing "Table" suffix
|
|
5542
|
+
name: "mydata"
|
|
5543
|
+
name: "my_data"
|
|
5544
|
+
\`\`\`
|
|
5545
|
+
|
|
5546
|
+
**Reserved column names** - Cannot use these as column names:
|
|
5547
|
+
- \`id\` (auto-generated)
|
|
5548
|
+
- \`createdAt\` (auto-generated)
|
|
5549
|
+
- \`updatedAt\` (auto-generated)
|
|
5550
|
+
- \`computed\`
|
|
5551
|
+
- \`stale\`
|
|
5552
|
+
|
|
5553
|
+
\`\`\`typescript
|
|
5554
|
+
// ❌ WRONG - Using reserved column name
|
|
5555
|
+
columns: {
|
|
5556
|
+
createdAt: z.string() // Reserved!
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
// ✅ CORRECT - Use alternative name
|
|
5560
|
+
columns: {
|
|
5561
|
+
savedAt: z.string()
|
|
5562
|
+
}
|
|
5563
|
+
\`\`\`
|
|
5564
|
+
|
|
5565
|
+
### Auto-Registration
|
|
5566
|
+
|
|
5567
|
+
Files in \`src/tables/\` are **auto-registered** by the ADK. Do NOT re-export from index.ts:
|
|
5568
|
+
|
|
5569
|
+
\`\`\`typescript
|
|
5570
|
+
// src/tables/index.ts
|
|
5571
|
+
// ❌ WRONG - Causes duplicate registration errors
|
|
5572
|
+
export { MyTable } from "./myTable";
|
|
5573
|
+
|
|
5574
|
+
// ✅ CORRECT - Leave empty or add comment
|
|
5575
|
+
// Tables are auto-registered from src/tables/*.ts files
|
|
5576
|
+
\`\`\`
|
|
5577
|
+
|
|
5578
|
+
Same applies to \`src/conversations/\`, \`src/workflows/\`, \`src/triggers/\`, etc.
|
|
5579
|
+
|
|
5580
|
+
Beyond basic CRUD, Tables support powerful query and manipulation features:
|
|
5581
|
+
|
|
5582
|
+
### Complex Filtering
|
|
5583
|
+
|
|
5584
|
+
Use logical operators and conditions:
|
|
5585
|
+
|
|
5586
|
+
\`\`\`typescript
|
|
5587
|
+
await MyTable.findRows({
|
|
5588
|
+
filter: {
|
|
5589
|
+
$and: [
|
|
5590
|
+
{ status: 'open' },
|
|
5591
|
+
{ priority: { $in: ['high', 'urgent'] } }
|
|
5592
|
+
],
|
|
5593
|
+
$or: [
|
|
5594
|
+
{ assignee: userId },
|
|
5595
|
+
{ reporter: userId }
|
|
5596
|
+
],
|
|
5597
|
+
title: { $regex: 'bug|error', $options: 'i' }
|
|
5598
|
+
}
|
|
5599
|
+
})
|
|
5600
|
+
\`\`\`
|
|
5601
|
+
|
|
5602
|
+
**Filter operators:**
|
|
5603
|
+
- \`$eq\`, \`$ne\` - Equal, not equal
|
|
5604
|
+
- \`$gt\`, \`$gte\`, \`$lt\`, \`$lte\` - Comparisons
|
|
5605
|
+
- \`$in\`, \`$nin\` - In array, not in array
|
|
5606
|
+
- \`$exists\` - Field exists
|
|
5607
|
+
- \`$regex\` - Regular expression match
|
|
5608
|
+
- \`$options\` - Regex options (e.g., 'i' for case-insensitive)
|
|
5609
|
+
- \`$and\`, \`$or\` - Logical operators
|
|
5610
|
+
|
|
5611
|
+
### Full-Text Search
|
|
5612
|
+
|
|
5613
|
+
Search across searchable columns:
|
|
5614
|
+
|
|
5615
|
+
\`\`\`typescript
|
|
5616
|
+
await MyTable.findRows({
|
|
5617
|
+
search: 'query string',
|
|
5618
|
+
filter: { status: 'active' }
|
|
5619
|
+
})
|
|
5620
|
+
\`\`\`
|
|
5621
|
+
|
|
5622
|
+
Mark columns as searchable in schema:
|
|
5623
|
+
|
|
5624
|
+
\`\`\`typescript
|
|
5625
|
+
columns: {
|
|
5626
|
+
title: z.string().searchable(),
|
|
5627
|
+
description: z.string().searchable()
|
|
5628
|
+
}
|
|
5629
|
+
\`\`\`
|
|
5630
|
+
|
|
5631
|
+
### Aggregation and Grouping
|
|
5632
|
+
|
|
5633
|
+
Group and aggregate data:
|
|
5634
|
+
|
|
5635
|
+
\`\`\`typescript
|
|
5636
|
+
await MyTable.findRows({
|
|
5637
|
+
group: {
|
|
5638
|
+
status: 'count',
|
|
5639
|
+
priority: ['sum', 'avg'],
|
|
5640
|
+
complexity: ['max', 'min']
|
|
5641
|
+
}
|
|
5642
|
+
})
|
|
5643
|
+
\`\`\`
|
|
5644
|
+
|
|
5645
|
+
**Aggregation operations:** \`key\`, \`count\`, \`sum\`, \`avg\`, \`max\`, \`min\`, \`unique\`
|
|
5646
|
+
|
|
5647
|
+
### Computed Columns
|
|
5648
|
+
|
|
5649
|
+
Columns with values computed from row data:
|
|
5650
|
+
|
|
5651
|
+
\`\`\`typescript
|
|
5652
|
+
columns: {
|
|
5653
|
+
fullName: {
|
|
5654
|
+
computed: true,
|
|
5655
|
+
schema: z.string(),
|
|
5656
|
+
dependencies: ['firstName', 'lastName'],
|
|
5657
|
+
value: async (row) => \`\${row.firstName} \${row.lastName}\`
|
|
5658
|
+
},
|
|
5659
|
+
age: {
|
|
5660
|
+
computed: true,
|
|
5661
|
+
schema: z.number(),
|
|
5662
|
+
dependencies: ['birthDate'],
|
|
5663
|
+
value: async (row) => {
|
|
5664
|
+
const today = new Date()
|
|
5665
|
+
const birth = new Date(row.birthDate)
|
|
5666
|
+
return today.getFullYear() - birth.getFullYear()
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
\`\`\`
|
|
5671
|
+
|
|
5672
|
+
### Upsert Operations
|
|
5673
|
+
|
|
5674
|
+
Insert or update based on key column:
|
|
5675
|
+
|
|
5676
|
+
\`\`\`typescript
|
|
5677
|
+
await MyTable.upsertRows({
|
|
5678
|
+
rows: [
|
|
5679
|
+
{ externalId: '123', name: 'Item 1' },
|
|
5680
|
+
{ externalId: '456', name: 'Item 2' }
|
|
5681
|
+
],
|
|
5682
|
+
keyColumn: 'externalId', // Update if exists, insert if not
|
|
5683
|
+
waitComputed: true // Wait for computed columns to update
|
|
5684
|
+
})
|
|
5685
|
+
\`\`\`
|
|
5686
|
+
|
|
5687
|
+
### Bulk Operations
|
|
5688
|
+
|
|
5689
|
+
Efficient batch operations:
|
|
5690
|
+
|
|
5691
|
+
\`\`\`typescript
|
|
5692
|
+
// Delete by filter
|
|
5693
|
+
await MyTable.deleteRows({
|
|
5694
|
+
filter: { status: 'archived', createdAt: { $lt: '2024-01-01' } }
|
|
5695
|
+
})
|
|
5696
|
+
|
|
5697
|
+
// Delete by IDs
|
|
5698
|
+
await MyTable.deleteRowIds([1, 2, 3])
|
|
5699
|
+
|
|
5700
|
+
// Delete all
|
|
5701
|
+
await MyTable.deleteAllRows()
|
|
5702
|
+
|
|
5703
|
+
// Update multiple
|
|
5704
|
+
await MyTable.updateRows({
|
|
5705
|
+
rows: [
|
|
5706
|
+
{ id: 1, status: 'active' },
|
|
5707
|
+
{ id: 2, status: 'inactive' }
|
|
5708
|
+
],
|
|
5709
|
+
waitComputed: true
|
|
5710
|
+
})
|
|
5711
|
+
\`\`\`
|
|
5712
|
+
|
|
5713
|
+
### Error Handling
|
|
5714
|
+
|
|
5715
|
+
Collect errors and warnings from bulk operations:
|
|
5716
|
+
|
|
5717
|
+
\`\`\`typescript
|
|
5718
|
+
const { errors, warnings } = await MyTable.createRows({
|
|
5719
|
+
rows: data,
|
|
5720
|
+
waitComputed: true
|
|
5721
|
+
})
|
|
5722
|
+
|
|
5723
|
+
if (errors?.length) {
|
|
5724
|
+
console.error('Failed rows:', errors)
|
|
5725
|
+
}
|
|
5726
|
+
if (warnings?.length) {
|
|
5727
|
+
console.warn('Warnings:', warnings)
|
|
5728
|
+
}
|
|
5729
|
+
\`\`\`
|
|
5730
|
+
|
|
5731
|
+
## Knowledge Base Operations
|
|
5732
|
+
|
|
5733
|
+
### Data Sources
|
|
5734
|
+
|
|
5735
|
+
Multiple source types for knowledge bases:
|
|
5736
|
+
|
|
5737
|
+
#### Directory Source
|
|
5738
|
+
|
|
5739
|
+
\`\`\`typescript
|
|
5740
|
+
import { DataSource } from '@botpress/runtime'
|
|
5741
|
+
|
|
5742
|
+
const docs = DataSource.Directory.fromPath('src/knowledge', {
|
|
5743
|
+
id: 'docs',
|
|
5744
|
+
filter: (path) => path.endsWith('.md') || path.endsWith('.txt')
|
|
5745
|
+
})
|
|
5746
|
+
\`\`\`
|
|
5747
|
+
|
|
5748
|
+
#### Website Source
|
|
5749
|
+
|
|
5750
|
+
\`\`\`typescript
|
|
5751
|
+
const siteDocs = DataSource.Website.fromSitemap('https://example.com/sitemap.xml', {
|
|
5752
|
+
id: 'website',
|
|
5753
|
+
maxPages: 500,
|
|
5754
|
+
fetch: 'node:fetch' // or custom fetch implementation
|
|
5755
|
+
})
|
|
5756
|
+
\`\`\`
|
|
5757
|
+
|
|
5758
|
+
### Knowledge Base Definition
|
|
5759
|
+
|
|
5760
|
+
\`\`\`typescript
|
|
5761
|
+
import { Knowledge } from '@botpress/runtime'
|
|
5762
|
+
|
|
5763
|
+
export default new Knowledge({
|
|
5764
|
+
name: 'docs',
|
|
5765
|
+
description: 'Product documentation',
|
|
5766
|
+
sources: [docsDirectory, websiteSource]
|
|
5767
|
+
})
|
|
5768
|
+
\`\`\`
|
|
5769
|
+
|
|
5770
|
+
### Refresh Operations
|
|
5771
|
+
|
|
5772
|
+
Manually refresh knowledge base content:
|
|
5773
|
+
|
|
5774
|
+
\`\`\`typescript
|
|
5775
|
+
// Refresh entire knowledge base
|
|
5776
|
+
await DocsKB.refresh({ force: true })
|
|
5777
|
+
|
|
5778
|
+
// Refresh specific source
|
|
5779
|
+
await DocsKB.refreshSource('website', { force: true })
|
|
5780
|
+
\`\`\`
|
|
5781
|
+
|
|
5782
|
+
**Options:**
|
|
5783
|
+
- \`force: true\` - Force refresh even if recently updated
|
|
5784
|
+
- Automatic refresh via scheduled workflows recommended
|
|
5785
|
+
|
|
5786
|
+
### Using Knowledge in Execute
|
|
5787
|
+
|
|
5788
|
+
\`\`\`typescript
|
|
5789
|
+
await execute({
|
|
5790
|
+
instructions: 'Answer using the documentation',
|
|
5791
|
+
knowledge: [DocsKB, APIKB],
|
|
5792
|
+
tools: [searchTool]
|
|
5793
|
+
})
|
|
5794
|
+
\`\`\`
|
|
5795
|
+
|
|
5796
|
+
Knowledge bases are automatically searchable via the \`search_knowledge\` tool.
|
|
5797
|
+
|
|
5798
|
+
## Advanced Conversation Patterns
|
|
5799
|
+
|
|
5800
|
+
### Multiple Channel Support
|
|
5801
|
+
|
|
5802
|
+
Handle messages from multiple channels in one handler:
|
|
5803
|
+
|
|
5804
|
+
\`\`\`typescript
|
|
5805
|
+
export default new Conversation({
|
|
5806
|
+
channel: ['chat.channel', 'webchat.channel', 'slack.dm'],
|
|
5807
|
+
handler: async ({ channel, execute }) => {
|
|
5808
|
+
console.log(\`Message from: \${channel}\`)
|
|
5809
|
+
await execute({ instructions: '...' })
|
|
5810
|
+
}
|
|
5811
|
+
})
|
|
5812
|
+
\`\`\`
|
|
5813
|
+
|
|
5814
|
+
### Event Handling
|
|
5815
|
+
|
|
5816
|
+
Subscribe to integration events:
|
|
5817
|
+
|
|
5818
|
+
\`\`\`typescript
|
|
5819
|
+
export default new Conversation({
|
|
5820
|
+
channel: 'webchat.channel',
|
|
5821
|
+
events: ['webchat:conversationStarted', 'webchat:conversationEnded'],
|
|
5822
|
+
handler: async ({ type, event, message }) => {
|
|
5823
|
+
if (type === 'event' && event.type === 'webchat:conversationStarted') {
|
|
5824
|
+
// Send welcome message
|
|
5825
|
+
await conversation.send({
|
|
5826
|
+
type: 'text',
|
|
5827
|
+
payload: { text: 'Welcome!' }
|
|
5828
|
+
})
|
|
5829
|
+
}
|
|
5830
|
+
|
|
5831
|
+
if (type === 'message' && message?.type === 'text') {
|
|
5832
|
+
// Handle regular messages
|
|
5833
|
+
await execute({ instructions: '...' })
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
})
|
|
5837
|
+
\`\`\`
|
|
5838
|
+
|
|
5839
|
+
### Workflow Request Handling
|
|
5840
|
+
|
|
5841
|
+
Handle data requests from workflows:
|
|
5842
|
+
|
|
5843
|
+
\`\`\`typescript
|
|
5844
|
+
import { isWorkflowDataRequest } from '@botpress/runtime'
|
|
5845
|
+
|
|
5846
|
+
handler: async ({ type, event, execute }) => {
|
|
5847
|
+
// Check if this is a workflow requesting data
|
|
5848
|
+
if (type === 'workflow_request' && isWorkflowDataRequest(event)) {
|
|
5849
|
+
const userInput = await promptUser(event.payload.message)
|
|
5850
|
+
|
|
5851
|
+
// Provide data back to workflow
|
|
5852
|
+
await workflow.provide(event, { topic: userInput })
|
|
5853
|
+
return
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
// Regular message handling
|
|
5857
|
+
await execute({ instructions: '...' })
|
|
5858
|
+
}
|
|
5859
|
+
\`\`\`
|
|
5860
|
+
|
|
5861
|
+
### Typed Workflow Interactions
|
|
5862
|
+
|
|
5863
|
+
Work with typed workflow instances:
|
|
5864
|
+
|
|
5865
|
+
\`\`\`typescript
|
|
5866
|
+
import { isWorkflow, ResearchWorkflow } from '@botpress/runtime'
|
|
5867
|
+
|
|
5868
|
+
handler: async ({ state }) => {
|
|
5869
|
+
if (state.research && isWorkflow(state.research, 'research')) {
|
|
5870
|
+
// state.research is now typed as ResearchWorkflow
|
|
5871
|
+
console.log(state.research.status)
|
|
5872
|
+
console.log(state.research.output) // Typed output
|
|
5873
|
+
|
|
5874
|
+
if (state.research.status === 'completed') {
|
|
5875
|
+
await conversation.send({
|
|
5876
|
+
type: 'text',
|
|
5877
|
+
payload: { text: state.research.output.result }
|
|
5878
|
+
})
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
\`\`\`
|
|
5883
|
+
|
|
5884
|
+
### Dynamic Tools Based on State
|
|
5885
|
+
|
|
5886
|
+
Provide different tools based on conversation state:
|
|
5887
|
+
|
|
5888
|
+
\`\`\`typescript
|
|
5889
|
+
handler: async ({ state, execute }) => {
|
|
5890
|
+
const tools = () => {
|
|
5891
|
+
if (state.workflowRunning) {
|
|
5892
|
+
return [cancelWorkflowTool, checkStatusTool]
|
|
5893
|
+
} else {
|
|
5894
|
+
return [startWorkflowTool, browseTool, searchTool]
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
|
|
5898
|
+
await execute({
|
|
5899
|
+
instructions: '...',
|
|
5900
|
+
tools: tools()
|
|
5901
|
+
})
|
|
5902
|
+
}
|
|
5903
|
+
\`\`\`
|
|
5904
|
+
|
|
5905
|
+
### Message Sending
|
|
5906
|
+
|
|
5907
|
+
Send different message types:
|
|
5908
|
+
|
|
5909
|
+
\`\`\`typescript
|
|
5910
|
+
// Text message
|
|
5911
|
+
await conversation.send({
|
|
5912
|
+
type: 'text',
|
|
5913
|
+
payload: { text: 'Hello!' }
|
|
5914
|
+
})
|
|
5915
|
+
|
|
5916
|
+
// Custom message type (integration-specific)
|
|
5917
|
+
await conversation.send({
|
|
5918
|
+
type: 'custom:messageType',
|
|
5919
|
+
payload: { data: 'custom payload' }
|
|
5920
|
+
})
|
|
5921
|
+
\`\`\`
|
|
5922
|
+
|
|
5923
|
+
## Citations System
|
|
5924
|
+
|
|
5925
|
+
Track and manage source citations for LLM responses:
|
|
5926
|
+
|
|
5927
|
+
### CitationsManager
|
|
5928
|
+
|
|
5929
|
+
Access via context:
|
|
5930
|
+
|
|
5931
|
+
\`\`\`typescript
|
|
5932
|
+
import { context } from '@botpress/runtime'
|
|
5933
|
+
|
|
5934
|
+
const citations = context.get('citations')
|
|
5935
|
+
\`\`\`
|
|
5936
|
+
|
|
5937
|
+
### Registering Sources
|
|
5938
|
+
|
|
5939
|
+
Register sources that can be cited:
|
|
5940
|
+
|
|
5941
|
+
\`\`\`typescript
|
|
5942
|
+
// Register with URL
|
|
5943
|
+
const { tag } = citations.registerSource({
|
|
5944
|
+
url: 'https://example.com/doc',
|
|
5945
|
+
title: 'Documentation Page'
|
|
5946
|
+
})
|
|
5947
|
+
|
|
5948
|
+
// Register with file reference
|
|
5949
|
+
const { tag } = citations.registerSource({
|
|
5950
|
+
file: fileKey,
|
|
5951
|
+
title: 'Internal Document'
|
|
5952
|
+
})
|
|
5953
|
+
\`\`\`
|
|
5954
|
+
|
|
5955
|
+
### Using Citation Tags
|
|
5956
|
+
|
|
5957
|
+
Inject citation tags into LLM content:
|
|
5958
|
+
|
|
5959
|
+
\`\`\`typescript
|
|
5960
|
+
const results = await searchKnowledgeBase(query)
|
|
5961
|
+
|
|
5962
|
+
for (const result of results) {
|
|
5963
|
+
const { tag } = citations.registerSource({
|
|
5964
|
+
file: result.file.key,
|
|
5965
|
+
title: result.file.name
|
|
5966
|
+
})
|
|
5967
|
+
|
|
5968
|
+
content += \`\${result.content} \${tag}\\n\`
|
|
5969
|
+
}
|
|
5970
|
+
|
|
5971
|
+
// Return cited content
|
|
5972
|
+
throw new ThinkSignal('results', content)
|
|
5973
|
+
\`\`\`
|
|
5974
|
+
|
|
5975
|
+
### Citation Format
|
|
5976
|
+
|
|
5977
|
+
Citations are automatically formatted with tags like \`[1]\`, \`[2]\`, etc., and tracked by the system for reference.
|
|
5978
|
+
|
|
5979
|
+
### Example: Tool with Citations
|
|
5980
|
+
|
|
5981
|
+
\`\`\`typescript
|
|
5982
|
+
export default new Autonomous.Tool({
|
|
5983
|
+
name: 'search_docs',
|
|
5984
|
+
description: 'Search documentation',
|
|
5985
|
+
handler: async ({ query }) => {
|
|
5986
|
+
const citations = context.get('citations')
|
|
5987
|
+
const results = await searchDocs(query)
|
|
5988
|
+
|
|
5989
|
+
let response = ''
|
|
5990
|
+
for (const doc of results) {
|
|
5991
|
+
const { tag } = citations.registerSource({
|
|
5992
|
+
url: doc.url,
|
|
5993
|
+
title: doc.title
|
|
5994
|
+
})
|
|
5995
|
+
response += \`\${doc.content} \${tag}\\n\\n\`
|
|
5996
|
+
}
|
|
5997
|
+
|
|
5998
|
+
return response
|
|
5999
|
+
}
|
|
6000
|
+
})
|
|
6001
|
+
\`\`\`
|
|
6002
|
+
|
|
6003
|
+
## Common Mistakes to Avoid
|
|
6004
|
+
|
|
6005
|
+
### 1. Wrong Zai Import
|
|
6006
|
+
\`\`\`typescript
|
|
6007
|
+
// ❌ WRONG
|
|
6008
|
+
import { zai } from '@botpress/runtime'
|
|
6009
|
+
const result = await zai.extract(...)
|
|
6010
|
+
|
|
6011
|
+
// ✅ CORRECT
|
|
6012
|
+
import { adk } from '@botpress/runtime'
|
|
6013
|
+
const result = await adk.zai.extract(...)
|
|
6014
|
+
\`\`\`
|
|
6015
|
+
|
|
6016
|
+
### 2. Expecting \`.output\` from zai.extract
|
|
6017
|
+
\`\`\`typescript
|
|
6018
|
+
// ❌ WRONG - zai.extract returns data directly
|
|
6019
|
+
const result = await adk.zai.extract(input, schema)
|
|
6020
|
+
console.log(result.output) // undefined!
|
|
6021
|
+
|
|
6022
|
+
// ✅ CORRECT
|
|
6023
|
+
const result = await adk.zai.extract(input, schema)
|
|
6024
|
+
console.log(result) // { name: "John", age: 30 }
|
|
6025
|
+
\`\`\`
|
|
6026
|
+
|
|
6027
|
+
### 3. Wrong Exit Result Handling
|
|
6028
|
+
\`\`\`typescript
|
|
6029
|
+
// ❌ WRONG
|
|
6030
|
+
if (result.exit?.name === 'my_exit') {
|
|
6031
|
+
const data = result.exit.value
|
|
6032
|
+
}
|
|
6033
|
+
|
|
6034
|
+
// ✅ CORRECT
|
|
6035
|
+
if (result.is(MyExit)) {
|
|
6036
|
+
const data = result.output // Type-safe!
|
|
6037
|
+
}
|
|
6038
|
+
\`\`\`
|
|
6039
|
+
|
|
6040
|
+
### 4. Reserved Table Column Names
|
|
6041
|
+
\`\`\`typescript
|
|
6042
|
+
// ❌ WRONG - These are reserved
|
|
6043
|
+
columns: {
|
|
6044
|
+
id: z.string(),
|
|
6045
|
+
createdAt: z.string(),
|
|
6046
|
+
updatedAt: z.string()
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
// ✅ CORRECT - Use alternatives
|
|
6050
|
+
columns: {
|
|
6051
|
+
visibleId: z.string(),
|
|
6052
|
+
savedAt: z.string(),
|
|
6053
|
+
modifiedAt: z.string()
|
|
6054
|
+
}
|
|
6055
|
+
\`\`\`
|
|
6056
|
+
|
|
6057
|
+
### 5. Re-exporting Auto-Registered Files
|
|
6058
|
+
\`\`\`typescript
|
|
6059
|
+
// ❌ WRONG - src/tables/index.ts
|
|
6060
|
+
export { MyTable } from "./myTable" // Causes duplicates!
|
|
6061
|
+
|
|
6062
|
+
// ✅ CORRECT - Leave index.ts empty
|
|
6063
|
+
// Files in src/tables/, src/conversations/, etc. are auto-registered
|
|
6064
|
+
\`\`\`
|
|
6065
|
+
|
|
6066
|
+
### 6. Table Name Missing "Table" Suffix
|
|
6067
|
+
\`\`\`typescript
|
|
6068
|
+
// ❌ WRONG
|
|
6069
|
+
name: "users"
|
|
6070
|
+
name: "user_data"
|
|
6071
|
+
|
|
6072
|
+
// ✅ CORRECT
|
|
6073
|
+
name: "usersTable"
|
|
6074
|
+
name: "userdataTable"
|
|
6075
|
+
\`\`\`
|
|
6076
|
+
|
|
6077
|
+
## When Making Changes
|
|
6078
|
+
|
|
6079
|
+
1. **Always search Botpress docs** using \`mcp__botpress-docs__SearchBotpress\`
|
|
6080
|
+
2. **Check examples** for patterns
|
|
6081
|
+
3. **Regenerate types** after changing \`agent.config.ts\` (run \`adk dev\`)
|
|
6082
|
+
4. **Test in dev mode** with hot reloading (\`adk dev\`)
|
|
6083
|
+
5. **Follow TypeScript types** - They're auto-generated from integrations
|
|
6084
|
+
|
|
6085
|
+
## Resources
|
|
6086
|
+
|
|
6087
|
+
- [ADK Overview](https://botpress.com/docs/for-developers/adk/overview)
|
|
6088
|
+
- [ADK Getting Started](https://botpress.com/docs/for-developers/adk/getting-started)
|
|
6089
|
+
- [Project Structure](https://botpress.com/docs/for-developers/adk/project-structure)
|
|
6090
|
+
- [Conversations](https://botpress.com/docs/for-developers/adk/concepts/conversations)
|
|
6091
|
+
- [Workflows](https://botpress.com/docs/for-developers/adk/concepts/workflows)
|
|
6092
|
+
`;
|
|
6093
|
+
|
|
6094
|
+
// src/agent-init/agent-project-generator.ts
|
|
4834
6095
|
class AgentProjectGenerator {
|
|
4835
6096
|
projectPath;
|
|
4836
6097
|
projectName;
|
|
@@ -4875,7 +6136,7 @@ class AgentProjectGenerator {
|
|
|
4875
6136
|
deploy: "adk deploy"
|
|
4876
6137
|
},
|
|
4877
6138
|
dependencies: {
|
|
4878
|
-
"@botpress/runtime": `^${"1.11.
|
|
6139
|
+
"@botpress/runtime": `^${"1.11.9"}`
|
|
4879
6140
|
},
|
|
4880
6141
|
devDependencies: {
|
|
4881
6142
|
typescript: "^5.9.3"
|
|
@@ -5039,9 +6300,7 @@ A Botpress Agent built with the ADK.
|
|
|
5039
6300
|
await this.writeFormattedFile("README.md", readme);
|
|
5040
6301
|
}
|
|
5041
6302
|
createClaudeMd() {
|
|
5042
|
-
|
|
5043
|
-
const claudeMd = fs11.readFileSync(templatePath, "utf-8");
|
|
5044
|
-
this.writeFile("CLAUDE.md", claudeMd);
|
|
6303
|
+
this.writeFile("CLAUDE.md", CLAUDE_template_default);
|
|
5045
6304
|
}
|
|
5046
6305
|
async createSourceStructure() {
|
|
5047
6306
|
const srcPath = path15.join(this.projectPath, "src");
|
|
@@ -8840,7 +10099,7 @@ class KnowledgeManager {
|
|
|
8840
10099
|
}
|
|
8841
10100
|
}
|
|
8842
10101
|
// src/knowledge/sync-formatter.ts
|
|
8843
|
-
import { readFileSync
|
|
10102
|
+
import { readFileSync } from "fs";
|
|
8844
10103
|
import { join as join7 } from "path";
|
|
8845
10104
|
var getAdkVersion = () => {
|
|
8846
10105
|
try {
|
|
@@ -8849,7 +10108,7 @@ var getAdkVersion = () => {
|
|
|
8849
10108
|
} catch {
|
|
8850
10109
|
try {
|
|
8851
10110
|
const adkPackagePath = join7(process.cwd(), "node_modules/@botpress/adk/package.json");
|
|
8852
|
-
const pkg = JSON.parse(
|
|
10111
|
+
const pkg = JSON.parse(readFileSync(adkPackagePath, "utf-8"));
|
|
8853
10112
|
return pkg.version;
|
|
8854
10113
|
} catch {
|
|
8855
10114
|
return "unknown";
|
|
@@ -9137,7 +10396,7 @@ class AgentConfigSyncManager {
|
|
|
9137
10396
|
}
|
|
9138
10397
|
|
|
9139
10398
|
// src/preflight/formatter.ts
|
|
9140
|
-
import { readFileSync as
|
|
10399
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
9141
10400
|
import { join as join9 } from "path";
|
|
9142
10401
|
var getAdkVersion2 = () => {
|
|
9143
10402
|
try {
|
|
@@ -9146,7 +10405,7 @@ var getAdkVersion2 = () => {
|
|
|
9146
10405
|
} catch {
|
|
9147
10406
|
try {
|
|
9148
10407
|
const adkPackagePath = join9(process.cwd(), "node_modules/@botpress/adk/package.json");
|
|
9149
|
-
const pkg = JSON.parse(
|
|
10408
|
+
const pkg = JSON.parse(readFileSync2(adkPackagePath, "utf-8"));
|
|
9150
10409
|
return pkg.version;
|
|
9151
10410
|
} catch {
|
|
9152
10411
|
return "unknown";
|
|
@@ -9504,5 +10763,5 @@ export {
|
|
|
9504
10763
|
AgentProject
|
|
9505
10764
|
};
|
|
9506
10765
|
|
|
9507
|
-
//# debugId=
|
|
10766
|
+
//# debugId=4806E8ECDC510E9264756E2164756E21
|
|
9508
10767
|
//# sourceMappingURL=index.js.map
|