@falai/agent 0.5.3 → 0.5.4
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/README.md +11 -0
- package/dist/cjs/core/State.js +2 -1
- package/dist/cjs/core/State.js.map +1 -1
- package/dist/cjs/types/route.d.ts +2 -0
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/core/State.js +2 -1
- package/dist/core/State.js.map +1 -1
- package/dist/types/route.d.ts +2 -0
- package/dist/types/route.d.ts.map +1 -1
- package/docs/API_REFERENCE.md +83 -13
- package/docs/ARCHITECTURE.md +48 -1
- package/docs/PERSISTENCE.md +18 -6
- package/examples/custom-database-persistence.ts +533 -0
- package/examples/prisma-persistence.ts +13 -2
- package/package.json +1 -1
- package/src/core/State.ts +1 -1
- package/src/types/route.ts +2 -0
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
- **Session State Integration** - Automatic saving of extracted data & conversation progress
|
|
71
71
|
- **Provider Pattern** - Simple API like AI providers
|
|
72
72
|
- **Multiple Adapters** - Prisma, Redis, MongoDB, PostgreSQL, SQLite, OpenSearch, Memory
|
|
73
|
+
- **Custom Database Support** - Manual session state management for existing schemas
|
|
73
74
|
- **Auto-save** - Automatic session state & message persistence
|
|
74
75
|
- **Type-Safe** - Full TypeScript support with generics
|
|
75
76
|
- **Extensible** - Create adapters for any database
|
|
@@ -972,6 +973,16 @@ Full-text search and analytics-powered persistence:
|
|
|
972
973
|
- 🌐 Compatible with Elasticsearch 7.x
|
|
973
974
|
- ☁️ AWS OpenSearch Service ready
|
|
974
975
|
|
|
976
|
+
### 🗄️ [Custom Database Integration](./examples/custom-database-persistence.ts)
|
|
977
|
+
|
|
978
|
+
Manual session state management for existing database schemas:
|
|
979
|
+
|
|
980
|
+
- 🔧 Full control over database operations
|
|
981
|
+
- 📦 Works with any database (no adapter needed)
|
|
982
|
+
- 🔄 Manual session state save/restore
|
|
983
|
+
- 🎯 Perfect for integrating with existing schemas
|
|
984
|
+
- ✅ Complete example with validation hooks
|
|
985
|
+
|
|
975
986
|
---
|
|
976
987
|
|
|
977
988
|
## 💾 Database Adapters
|
package/dist/cjs/core/State.js
CHANGED
|
@@ -46,7 +46,8 @@ class State {
|
|
|
46
46
|
return this.createStateRefWithTransition(spec.state);
|
|
47
47
|
}
|
|
48
48
|
// Create new target state for chatState or toolState
|
|
49
|
-
const targetState = new State(this.routeId, spec.chatState,
|
|
49
|
+
const targetState = new State(this.routeId, spec.chatState, spec.id, // Use custom ID if provided
|
|
50
|
+
spec.gather, spec.skipIf, spec.requiredData);
|
|
50
51
|
const transition = new Transition_1.Transition(this.getRef(), spec, condition);
|
|
51
52
|
transition.setTarget(targetState);
|
|
52
53
|
this.transitions.push(transition);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"State.js","sourceRoot":"","sources":["../../../src/core/State.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AASH,4CAAyC;AACzC,6CAA0C;AAC1C,oCAA8C;AAE9C;;GAEG;AACH,MAAa,KAAK;IAQhB,YACkB,OAAe,EACf,WAAoB,EACpC,QAAiB,EACjB,YAAuB,EACvB,MAAoD,EACpD,YAAuB;QALP,YAAO,GAAP,OAAO,CAAQ;QACf,gBAAW,GAAX,WAAW,CAAS;QAR9B,gBAAW,GAAuC,EAAE,CAAC;QACrD,eAAU,GAAgB,EAAE,CAAC;QAanC,kDAAkD;QAClD,IAAI,CAAC,EAAE,GAAG,QAAQ,IAAI,IAAA,oBAAe,EAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CACV,IAA0C,EAC1C,SAAkB;QAElB,mBAAmB;QACnB,IACE,IAAI,CAAC,KAAK;YACV,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;YAC9B,IAAI,CAAC,KAAK,KAAK,qBAAS,EACxB,CAAC;YACD,MAAM,aAAa,GAAG,IAAI,uBAAU,CAClC,IAAI,CAAC,MAAM,EAAE,EACb,EAAE,KAAK,EAAE,qBAAS,EAAE,EACpB,SAAS,CACV,CAAC;YACF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAErC,oCAAoC;YACpC,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAClC,CAAC;QAED,gCAAgC;QAChC,IAAI,IAAI,CAAC,KAAK,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACjD,MAAM,UAAU,GAAG,IAAI,uBAAU,CAC/B,IAAI,CAAC,MAAM,EAAE,EACb,IAAI,EACJ,SAAS,CACV,CAAC;YACF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAElC,OAAO,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,CAAC;QAED,qDAAqD;QACrD,MAAM,WAAW,GAAG,IAAI,KAAK,CAC3B,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,SAAS,EACd,
|
|
1
|
+
{"version":3,"file":"State.js","sourceRoot":"","sources":["../../../src/core/State.ts"],"names":[],"mappings":"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"}
|
|
@@ -64,6 +64,8 @@ export interface RouteOptions<TExtracted = unknown> {
|
|
|
64
64
|
* Specification for a state transition
|
|
65
65
|
*/
|
|
66
66
|
export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
|
|
67
|
+
/** Custom ID for this state (optional - will generate deterministic ID if not provided) */
|
|
68
|
+
id?: string;
|
|
67
69
|
/** Transition to a chat state with this description */
|
|
68
70
|
chatState?: string;
|
|
69
71
|
/** Transition to execute a tool */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,2FAA2F;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
|
package/dist/core/State.js
CHANGED
|
@@ -43,7 +43,8 @@ export class State {
|
|
|
43
43
|
return this.createStateRefWithTransition(spec.state);
|
|
44
44
|
}
|
|
45
45
|
// Create new target state for chatState or toolState
|
|
46
|
-
const targetState = new State(this.routeId, spec.chatState,
|
|
46
|
+
const targetState = new State(this.routeId, spec.chatState, spec.id, // Use custom ID if provided
|
|
47
|
+
spec.gather, spec.skipIf, spec.requiredData);
|
|
47
48
|
const transition = new Transition(this.getRef(), spec, condition);
|
|
48
49
|
transition.setTarget(targetState);
|
|
49
50
|
this.transitions.push(transition);
|
package/dist/core/State.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"State.js","sourceRoot":"","sources":["../../src/core/State.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,OAAO,KAAK;IAQhB,YACkB,OAAe,EACf,WAAoB,EACpC,QAAiB,EACjB,YAAuB,EACvB,MAAoD,EACpD,YAAuB;QALP,YAAO,GAAP,OAAO,CAAQ;QACf,gBAAW,GAAX,WAAW,CAAS;QAR9B,gBAAW,GAAuC,EAAE,CAAC;QACrD,eAAU,GAAgB,EAAE,CAAC;QAanC,kDAAkD;QAClD,IAAI,CAAC,EAAE,GAAG,QAAQ,IAAI,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CACV,IAA0C,EAC1C,SAAkB;QAElB,mBAAmB;QACnB,IACE,IAAI,CAAC,KAAK;YACV,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;YAC9B,IAAI,CAAC,KAAK,KAAK,SAAS,EACxB,CAAC;YACD,MAAM,aAAa,GAAG,IAAI,UAAU,CAClC,IAAI,CAAC,MAAM,EAAE,EACb,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,SAAS,CACV,CAAC;YACF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAErC,oCAAoC;YACpC,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAClC,CAAC;QAED,gCAAgC;QAChC,IAAI,IAAI,CAAC,KAAK,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACjD,MAAM,UAAU,GAAG,IAAI,UAAU,CAC/B,IAAI,CAAC,MAAM,EAAE,EACb,IAAI,EACJ,SAAS,CACV,CAAC;YACF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAElC,OAAO,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,CAAC;QAED,qDAAqD;QACrD,MAAM,WAAW,GAAG,IAAI,KAAK,CAC3B,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,SAAS,EACd,
|
|
1
|
+
{"version":3,"file":"State.js","sourceRoot":"","sources":["../../src/core/State.ts"],"names":[],"mappings":"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"}
|
package/dist/types/route.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export interface RouteOptions<TExtracted = unknown> {
|
|
|
64
64
|
* Specification for a state transition
|
|
65
65
|
*/
|
|
66
66
|
export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
|
|
67
|
+
/** Custom ID for this state (optional - will generate deterministic ID if not provided) */
|
|
68
|
+
id?: string;
|
|
67
69
|
/** Transition to a chat state with this description */
|
|
68
70
|
chatState?: string;
|
|
69
71
|
/** Transition to execute a tool */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,UAAU,GAAG,OAAO;IAChD,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;IACvC,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,gBAAgB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO;IACtE,2FAA2F;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IAEnC,SAAS,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACxE,SAAQ,QAAQ;IAChB,iCAAiC;IACjC,YAAY,EAAE,CACZ,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,KACf,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAC7C"}
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -87,29 +87,99 @@ interface RespondOutput {
|
|
|
87
87
|
- Enables "I changed my mind" scenarios with context-aware routing
|
|
88
88
|
- Automatically merges new extracted data with existing session data
|
|
89
89
|
|
|
90
|
-
**Example:**
|
|
90
|
+
**Example with Persistence Adapters:**
|
|
91
91
|
|
|
92
92
|
```typescript
|
|
93
|
+
import { createSession } from "@falai/agent";
|
|
94
|
+
|
|
95
|
+
// Using built-in persistence adapters
|
|
96
|
+
const { sessionData, sessionState } =
|
|
97
|
+
await persistence.createSessionWithState<FlightData>({
|
|
98
|
+
userId: "user_123",
|
|
99
|
+
agentName: "Travel Agent",
|
|
100
|
+
});
|
|
101
|
+
|
|
93
102
|
const response = await agent.respond({
|
|
94
|
-
history
|
|
103
|
+
history,
|
|
104
|
+
session: sessionState, // Auto-saves if autoSave: true
|
|
95
105
|
});
|
|
106
|
+
```
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
**Example with Custom Database (Manual):**
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { createSession, SessionState } from "@falai/agent";
|
|
112
|
+
|
|
113
|
+
// Load from your custom database
|
|
114
|
+
const dbSession = await yourDb.sessions.findOne({ id: sessionId });
|
|
115
|
+
|
|
116
|
+
// Restore or create session state
|
|
117
|
+
let agentSession: SessionState<YourDataType>;
|
|
118
|
+
|
|
119
|
+
if (dbSession && dbSession.currentRoute && dbSession.collectedData) {
|
|
120
|
+
// Restore existing session from database
|
|
121
|
+
agentSession = {
|
|
122
|
+
currentRoute: {
|
|
123
|
+
id: dbSession.currentRoute,
|
|
124
|
+
title:
|
|
125
|
+
dbSession.collectedData?.currentRouteTitle || dbSession.currentRoute,
|
|
126
|
+
enteredAt: new Date(),
|
|
127
|
+
},
|
|
128
|
+
currentState: dbSession.currentState
|
|
129
|
+
? {
|
|
130
|
+
id: dbSession.currentState,
|
|
131
|
+
description: dbSession.collectedData?.currentStateDescription,
|
|
132
|
+
enteredAt: new Date(),
|
|
133
|
+
}
|
|
134
|
+
: undefined,
|
|
135
|
+
extracted: dbSession.collectedData?.extracted || {},
|
|
136
|
+
routeHistory: dbSession.collectedData?.routeHistory || [],
|
|
137
|
+
metadata: {
|
|
138
|
+
sessionId: dbSession.id,
|
|
139
|
+
createdAt: dbSession.createdAt,
|
|
140
|
+
lastUpdatedAt: new Date(),
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
} else {
|
|
144
|
+
// Create new session
|
|
145
|
+
agentSession = createSession<YourDataType>({
|
|
146
|
+
sessionId: dbSession?.id || "new-session-id",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Use session in conversation
|
|
151
|
+
const response = await agent.respond({
|
|
152
|
+
history,
|
|
153
|
+
session: agentSession,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Manually save to your database
|
|
157
|
+
await yourDb.sessions.update({
|
|
158
|
+
id: dbSession.id,
|
|
159
|
+
currentRoute: response.session?.currentRoute?.id,
|
|
160
|
+
currentState: response.session?.currentState?.id,
|
|
161
|
+
collectedData: {
|
|
162
|
+
extracted: response.session?.extracted,
|
|
163
|
+
routeHistory: response.session?.routeHistory,
|
|
164
|
+
currentRouteTitle: response.session?.currentRoute?.title,
|
|
165
|
+
currentStateDescription: response.session?.currentState?.description,
|
|
166
|
+
metadata: response.session?.metadata,
|
|
167
|
+
},
|
|
168
|
+
lastMessageAt: new Date(),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Save message
|
|
172
|
+
await yourDb.messages.create({
|
|
173
|
+
sessionId: dbSession.id,
|
|
100
174
|
role: "agent",
|
|
101
175
|
content: response.message,
|
|
102
|
-
route: response.
|
|
103
|
-
state: response.
|
|
104
|
-
toolCalls: response.toolCalls || [],
|
|
176
|
+
route: response.session?.currentRoute?.id,
|
|
177
|
+
state: response.session?.currentState?.id,
|
|
105
178
|
});
|
|
106
|
-
|
|
107
|
-
// Check if conversation is complete
|
|
108
|
-
if (response.route?.title === END_ROUTE) {
|
|
109
|
-
await markSessionComplete(session.id);
|
|
110
|
-
}
|
|
111
179
|
```
|
|
112
180
|
|
|
181
|
+
See also: [Custom Database Integration Example](../examples/custom-database-persistence.ts)
|
|
182
|
+
|
|
113
183
|
##### `respondStream(input: RespondInput<TContext>): AsyncGenerator<StreamChunk>`
|
|
114
184
|
|
|
115
185
|
Generates an AI response as a real-time stream for better user experience. Provides the same structured output as `respond()` but delivers it incrementally.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -58,7 +58,11 @@ import {
|
|
|
58
58
|
mergeExtracted,
|
|
59
59
|
} from "@falai/agent";
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
// Create session with optional metadata including session ID
|
|
62
|
+
let session = createSession<FlightData>(sessionId, {
|
|
63
|
+
userId: "user_456",
|
|
64
|
+
createdAt: new Date(),
|
|
65
|
+
});
|
|
62
66
|
|
|
63
67
|
// Turn 1 - Extract data
|
|
64
68
|
const response1 = await agent.respond({ history, session });
|
|
@@ -67,6 +71,31 @@ session = response1.session!; // Updated with extracted data
|
|
|
67
71
|
// Turn 2 - User changes mind (always-on routing)
|
|
68
72
|
const response2 = await agent.respond({ history, session: response1.session });
|
|
69
73
|
session = response2.session!; // Route/state updated if user changed direction
|
|
74
|
+
|
|
75
|
+
// Access session metadata
|
|
76
|
+
console.log(session.metadata?.sessionId); // "unique-session-123"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Session with Persistence:**
|
|
80
|
+
|
|
81
|
+
When using persistence adapters, set the session ID from the database:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Create database session and in-memory session state
|
|
85
|
+
const { sessionData, sessionState } =
|
|
86
|
+
await persistence.createSessionWithState<FlightData>({
|
|
87
|
+
userId: "user_123",
|
|
88
|
+
agentName: "Travel Agent",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// sessionState.metadata.sessionId is automatically set to sessionData.id
|
|
92
|
+
console.log(sessionState.metadata?.sessionId); // "cuid_from_database"
|
|
93
|
+
|
|
94
|
+
// Use it in conversation
|
|
95
|
+
const response = await agent.respond({
|
|
96
|
+
history,
|
|
97
|
+
session: sessionState, // Auto-saves to database!
|
|
98
|
+
});
|
|
70
99
|
```
|
|
71
100
|
|
|
72
101
|
**Why?** Session state enables:
|
|
@@ -75,6 +104,7 @@ session = response2.session!; // Route/state updated if user changed direction
|
|
|
75
104
|
- **Context Awareness** - Router sees current progress and extracted data
|
|
76
105
|
- **Data Persistence** - Extracted data survives across turns
|
|
77
106
|
- **State Recovery** - Resume conversations from any point
|
|
107
|
+
- **Session Tracking** - Track conversations via session ID in database
|
|
78
108
|
|
|
79
109
|
### 3. 🔧 Code-Based State Logic
|
|
80
110
|
|
|
@@ -83,12 +113,14 @@ Use TypeScript functions instead of LLM conditions for deterministic flow:
|
|
|
83
113
|
```typescript
|
|
84
114
|
// State with smart bypassing based on extracted data
|
|
85
115
|
const askDestination = route.initialState.transitionTo({
|
|
116
|
+
id: "ask_destination", // Optional: custom state ID
|
|
86
117
|
chatState: "Ask where they want to fly",
|
|
87
118
|
gather: ["destination"],
|
|
88
119
|
skipIf: (extracted) => !!extracted.destination, // Code-based condition!
|
|
89
120
|
});
|
|
90
121
|
|
|
91
122
|
const askDate = askDestination.transitionTo({
|
|
123
|
+
id: "ask_date", // Optional: custom state ID for easier tracking
|
|
92
124
|
chatState: "Ask about travel dates",
|
|
93
125
|
gather: ["departureDate"],
|
|
94
126
|
skipIf: (extracted) => !!extracted.departureDate,
|
|
@@ -96,12 +128,27 @@ const askDate = askDestination.transitionTo({
|
|
|
96
128
|
});
|
|
97
129
|
```
|
|
98
130
|
|
|
131
|
+
**Custom State IDs:**
|
|
132
|
+
|
|
133
|
+
You can optionally provide custom IDs for states to make them easier to track and reference:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const confirmBooking = askDate.transitionTo({
|
|
137
|
+
id: "confirm_booking", // ✅ Custom ID instead of auto-generated
|
|
138
|
+
chatState: "Confirm all booking details",
|
|
139
|
+
requiredData: ["destination", "departureDate", "passengers"],
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
If you don't provide an ID, one is automatically generated from the route ID and state description.
|
|
144
|
+
|
|
99
145
|
**Why?** Code-based logic provides:
|
|
100
146
|
|
|
101
147
|
- **Predictability** - No fuzzy LLM interpretation of conditions
|
|
102
148
|
- **Performance** - No extra LLM calls for condition checking
|
|
103
149
|
- **Debugging** - Clear logic flow you can trace
|
|
104
150
|
- **Type Safety** - Full TypeScript support for data validation
|
|
151
|
+
- **Custom IDs** - Easier tracking and debugging with meaningful state identifiers
|
|
105
152
|
|
|
106
153
|
### 4. 🛠️ Tools with Data Access
|
|
107
154
|
|
package/docs/PERSISTENCE.md
CHANGED
|
@@ -121,11 +121,18 @@ const bookingRoute = agent.createRoute<BookingData>({
|
|
|
121
121
|
},
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
// Define states with smart data gathering
|
|
125
|
-
bookingRoute.initialState
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
// Define states with smart data gathering and custom IDs
|
|
125
|
+
bookingRoute.initialState
|
|
126
|
+
.transitionTo({
|
|
127
|
+
id: "collect_details", // ✅ Custom state ID for easier tracking
|
|
128
|
+
chatState: "Collect booking details",
|
|
129
|
+
gather: ["destination", "date", "passengers"],
|
|
130
|
+
})
|
|
131
|
+
.transitionTo({
|
|
132
|
+
id: "confirm_booking", // ✅ Custom state ID
|
|
133
|
+
chatState: "Confirm all details",
|
|
134
|
+
requiredData: ["destination", "date", "passengers"],
|
|
135
|
+
});
|
|
129
136
|
|
|
130
137
|
// Access persistence methods
|
|
131
138
|
const persistence = agent.getPersistenceManager();
|
|
@@ -137,17 +144,22 @@ const { sessionData, sessionState } =
|
|
|
137
144
|
agentName: "My Agent",
|
|
138
145
|
});
|
|
139
146
|
|
|
147
|
+
// Session ID is automatically set in metadata
|
|
148
|
+
console.log("Session ID:", sessionState.metadata?.sessionId);
|
|
149
|
+
// Outputs: sessionData.id (e.g., "cuid_abc123")
|
|
150
|
+
|
|
140
151
|
// Load history
|
|
141
152
|
const history = await persistence.loadSessionHistory(sessionData.id);
|
|
142
153
|
|
|
143
154
|
// Generate response with session state
|
|
144
155
|
const response = await agent.respond({
|
|
145
156
|
history,
|
|
146
|
-
session: sessionState, // Pass session state
|
|
157
|
+
session: sessionState, // Pass session state with ID
|
|
147
158
|
});
|
|
148
159
|
|
|
149
160
|
// Session state is auto-saved! ✨
|
|
150
161
|
console.log("Extracted data:", response.session?.extracted);
|
|
162
|
+
console.log("Current state ID:", response.session?.currentState?.id); // Custom or auto-generated ID
|
|
151
163
|
|
|
152
164
|
// Save message
|
|
153
165
|
await persistence.saveMessage({
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Custom Database Integration (Manual Session State Management)
|
|
3
|
+
*
|
|
4
|
+
* This example shows how to manually manage session state when using your own
|
|
5
|
+
* database structure instead of the built-in persistence adapters.
|
|
6
|
+
*
|
|
7
|
+
* Use this approach if you:
|
|
8
|
+
* - Have an existing database schema you want to integrate with
|
|
9
|
+
* - Need custom data structures beyond what adapters provide
|
|
10
|
+
* - Want full control over database operations
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
Agent,
|
|
15
|
+
GeminiProvider,
|
|
16
|
+
createMessageEvent,
|
|
17
|
+
EventSource,
|
|
18
|
+
createSession,
|
|
19
|
+
SessionState,
|
|
20
|
+
MessageEventData,
|
|
21
|
+
Event,
|
|
22
|
+
} from "../src/index";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Example: Your existing database structure
|
|
26
|
+
* This could be any ORM (Prisma, TypeORM, Drizzle) or query builder
|
|
27
|
+
*/
|
|
28
|
+
interface CustomDatabaseSession {
|
|
29
|
+
id: string;
|
|
30
|
+
userId: string;
|
|
31
|
+
currentRoute?: string;
|
|
32
|
+
currentState?: string;
|
|
33
|
+
collectedData?: {
|
|
34
|
+
extracted?: Record<string, unknown>;
|
|
35
|
+
routeHistory?: unknown[];
|
|
36
|
+
currentRouteTitle?: string;
|
|
37
|
+
currentStateDescription?: string;
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
createdAt: Date;
|
|
41
|
+
updatedAt: Date;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface CustomDatabaseMessage {
|
|
45
|
+
id: string;
|
|
46
|
+
sessionId: string;
|
|
47
|
+
userId: string;
|
|
48
|
+
role: "user" | "agent" | "system";
|
|
49
|
+
content: string;
|
|
50
|
+
route?: string;
|
|
51
|
+
state?: string;
|
|
52
|
+
createdAt: Date;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Mock database - replace with your actual database
|
|
56
|
+
class CustomDatabase {
|
|
57
|
+
private sessions: Map<string, CustomDatabaseSession> = new Map();
|
|
58
|
+
private messages: Map<string, CustomDatabaseMessage[]> = new Map();
|
|
59
|
+
|
|
60
|
+
async findSession(id: string): Promise<CustomDatabaseSession | null> {
|
|
61
|
+
return this.sessions.get(id) || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async createSession(
|
|
65
|
+
data: Omit<CustomDatabaseSession, "id" | "createdAt" | "updatedAt">
|
|
66
|
+
): Promise<CustomDatabaseSession> {
|
|
67
|
+
const session: CustomDatabaseSession = {
|
|
68
|
+
id: `session_${Date.now()}`,
|
|
69
|
+
...data,
|
|
70
|
+
createdAt: new Date(),
|
|
71
|
+
updatedAt: new Date(),
|
|
72
|
+
};
|
|
73
|
+
this.sessions.set(session.id, session);
|
|
74
|
+
return session;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async updateSession(
|
|
78
|
+
id: string,
|
|
79
|
+
data: Partial<CustomDatabaseSession>
|
|
80
|
+
): Promise<CustomDatabaseSession | null> {
|
|
81
|
+
const session = this.sessions.get(id);
|
|
82
|
+
if (!session) return null;
|
|
83
|
+
|
|
84
|
+
const updated = {
|
|
85
|
+
...session,
|
|
86
|
+
...data,
|
|
87
|
+
updatedAt: new Date(),
|
|
88
|
+
};
|
|
89
|
+
this.sessions.set(id, updated);
|
|
90
|
+
return updated;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async createMessage(
|
|
94
|
+
data: Omit<CustomDatabaseMessage, "id" | "createdAt">
|
|
95
|
+
): Promise<CustomDatabaseMessage> {
|
|
96
|
+
const message: CustomDatabaseMessage = {
|
|
97
|
+
id: `msg_${Date.now()}`,
|
|
98
|
+
...data,
|
|
99
|
+
createdAt: new Date(),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const messages = this.messages.get(data.sessionId) || [];
|
|
103
|
+
messages.push(message);
|
|
104
|
+
this.messages.set(data.sessionId, messages);
|
|
105
|
+
|
|
106
|
+
return message;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async getSessionMessages(
|
|
110
|
+
sessionId: string
|
|
111
|
+
): Promise<CustomDatabaseMessage[]> {
|
|
112
|
+
return this.messages.get(sessionId) || [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Example data type for a customer onboarding route
|
|
117
|
+
interface OnboardingData {
|
|
118
|
+
fullName: string;
|
|
119
|
+
email: string;
|
|
120
|
+
companyName: string;
|
|
121
|
+
phoneNumber?: string;
|
|
122
|
+
industry?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function example() {
|
|
126
|
+
const db = new CustomDatabase();
|
|
127
|
+
const userId = "user_123";
|
|
128
|
+
|
|
129
|
+
// Create agent
|
|
130
|
+
const agent = new Agent({
|
|
131
|
+
name: "Onboarding Assistant",
|
|
132
|
+
description: "Help new customers get started",
|
|
133
|
+
goal: "Collect customer information efficiently",
|
|
134
|
+
ai: new GeminiProvider({
|
|
135
|
+
apiKey: process.env.GEMINI_API_KEY!,
|
|
136
|
+
model: "models/gemini-2.0-flash-exp",
|
|
137
|
+
}),
|
|
138
|
+
// NOTE: No persistence adapter - we handle it manually!
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Create onboarding route
|
|
142
|
+
const onboardingRoute = agent.createRoute<OnboardingData>({
|
|
143
|
+
title: "Customer Onboarding",
|
|
144
|
+
description: "Collect customer information",
|
|
145
|
+
conditions: [
|
|
146
|
+
"User is a new customer",
|
|
147
|
+
"User needs to set up their account",
|
|
148
|
+
],
|
|
149
|
+
gatherSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
fullName: { type: "string" },
|
|
153
|
+
email: { type: "string" },
|
|
154
|
+
companyName: { type: "string" },
|
|
155
|
+
phoneNumber: { type: "string" },
|
|
156
|
+
industry: { type: "string" },
|
|
157
|
+
},
|
|
158
|
+
required: ["fullName", "email", "companyName"],
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Define states with custom IDs
|
|
163
|
+
onboardingRoute.initialState
|
|
164
|
+
.transitionTo({
|
|
165
|
+
id: "ask_name",
|
|
166
|
+
chatState: "Ask for full name",
|
|
167
|
+
gather: ["fullName"],
|
|
168
|
+
skipIf: (data) => !!data.fullName,
|
|
169
|
+
})
|
|
170
|
+
.transitionTo({
|
|
171
|
+
id: "ask_email",
|
|
172
|
+
chatState: "Ask for email address",
|
|
173
|
+
gather: ["email"],
|
|
174
|
+
skipIf: (data) => !!data.email,
|
|
175
|
+
})
|
|
176
|
+
.transitionTo({
|
|
177
|
+
id: "ask_company",
|
|
178
|
+
chatState: "Ask for company name",
|
|
179
|
+
gather: ["companyName"],
|
|
180
|
+
skipIf: (data) => !!data.companyName,
|
|
181
|
+
})
|
|
182
|
+
.transitionTo({
|
|
183
|
+
id: "ask_phone",
|
|
184
|
+
chatState: "Ask for phone number (optional)",
|
|
185
|
+
gather: ["phoneNumber"],
|
|
186
|
+
})
|
|
187
|
+
.transitionTo({
|
|
188
|
+
id: "ask_industry",
|
|
189
|
+
chatState: "Ask for industry",
|
|
190
|
+
gather: ["industry"],
|
|
191
|
+
})
|
|
192
|
+
.transitionTo({
|
|
193
|
+
id: "confirm_details",
|
|
194
|
+
chatState: "Confirm all details",
|
|
195
|
+
requiredData: ["fullName", "email", "companyName"],
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Create or load session from your custom database
|
|
200
|
+
*/
|
|
201
|
+
let dbSession = await db.findSession("existing_session_id");
|
|
202
|
+
|
|
203
|
+
if (!dbSession) {
|
|
204
|
+
// Create new session in your database
|
|
205
|
+
dbSession = await db.createSession({
|
|
206
|
+
userId,
|
|
207
|
+
collectedData: {},
|
|
208
|
+
});
|
|
209
|
+
console.log("✨ Created new database session:", dbSession.id);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Convert database session to agent SessionState
|
|
214
|
+
*/
|
|
215
|
+
let agentSession: SessionState<OnboardingData>;
|
|
216
|
+
|
|
217
|
+
if (dbSession.currentRoute && dbSession.collectedData) {
|
|
218
|
+
// Restore existing session from database
|
|
219
|
+
console.log("📥 Restoring session from database...");
|
|
220
|
+
|
|
221
|
+
agentSession = {
|
|
222
|
+
currentRoute: {
|
|
223
|
+
id: dbSession.currentRoute,
|
|
224
|
+
title:
|
|
225
|
+
dbSession.collectedData?.currentRouteTitle || dbSession.currentRoute,
|
|
226
|
+
enteredAt: new Date(),
|
|
227
|
+
},
|
|
228
|
+
currentState: dbSession.currentState
|
|
229
|
+
? {
|
|
230
|
+
id: dbSession.currentState,
|
|
231
|
+
description: dbSession.collectedData?.currentStateDescription,
|
|
232
|
+
enteredAt: new Date(),
|
|
233
|
+
}
|
|
234
|
+
: undefined,
|
|
235
|
+
extracted:
|
|
236
|
+
(dbSession.collectedData?.extracted as Partial<OnboardingData>) || {},
|
|
237
|
+
routeHistory:
|
|
238
|
+
(dbSession.collectedData
|
|
239
|
+
?.routeHistory as SessionState<OnboardingData>["routeHistory"]) || [],
|
|
240
|
+
metadata: {
|
|
241
|
+
sessionId: dbSession.id,
|
|
242
|
+
userId,
|
|
243
|
+
createdAt: dbSession.createdAt,
|
|
244
|
+
lastUpdatedAt: dbSession.updatedAt,
|
|
245
|
+
...(dbSession.collectedData?.metadata as Record<string, unknown>),
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
console.log("✅ Session restored:", {
|
|
250
|
+
sessionId: agentSession.metadata?.sessionId,
|
|
251
|
+
currentRoute: agentSession.currentRoute?.title,
|
|
252
|
+
currentState: agentSession.currentState?.id,
|
|
253
|
+
extracted: agentSession.extracted,
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
// Create new session state
|
|
257
|
+
console.log("🆕 Creating new session state...");
|
|
258
|
+
|
|
259
|
+
agentSession = createSession<OnboardingData>(dbSession.id, {
|
|
260
|
+
sessionId: dbSession.id,
|
|
261
|
+
userId,
|
|
262
|
+
createdAt: dbSession.createdAt,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Simulate conversation
|
|
268
|
+
*/
|
|
269
|
+
const history: Event<MessageEventData>[] = [];
|
|
270
|
+
|
|
271
|
+
// Turn 1: User provides name and email
|
|
272
|
+
console.log("\n--- Turn 1 ---");
|
|
273
|
+
const userMessage1 = createMessageEvent(
|
|
274
|
+
EventSource.CUSTOMER,
|
|
275
|
+
"User",
|
|
276
|
+
"Hi! I'm John Smith and my email is john@acme.com"
|
|
277
|
+
);
|
|
278
|
+
history.push(userMessage1);
|
|
279
|
+
|
|
280
|
+
// Save user message to database
|
|
281
|
+
await db.createMessage({
|
|
282
|
+
sessionId: dbSession.id,
|
|
283
|
+
userId,
|
|
284
|
+
role: "user",
|
|
285
|
+
content: userMessage1.data.message,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const response1 = await agent.respond({
|
|
289
|
+
history,
|
|
290
|
+
session: agentSession,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
console.log("🤖 Agent:", response1.message);
|
|
294
|
+
console.log("📊 Extracted so far:", response1.session?.extracted);
|
|
295
|
+
|
|
296
|
+
// Save agent message to database
|
|
297
|
+
await db.createMessage({
|
|
298
|
+
sessionId: dbSession.id,
|
|
299
|
+
userId,
|
|
300
|
+
role: "agent",
|
|
301
|
+
content: response1.message,
|
|
302
|
+
route: response1.session?.currentRoute?.id,
|
|
303
|
+
state: response1.session?.currentState?.id,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Manually save session state back to database
|
|
307
|
+
await db.updateSession(dbSession.id, {
|
|
308
|
+
currentRoute: response1.session?.currentRoute?.id,
|
|
309
|
+
currentState: response1.session?.currentState?.id,
|
|
310
|
+
collectedData: {
|
|
311
|
+
extracted: response1.session?.extracted,
|
|
312
|
+
routeHistory: response1.session?.routeHistory,
|
|
313
|
+
currentRouteTitle: response1.session?.currentRoute?.title,
|
|
314
|
+
currentStateDescription: response1.session?.currentState?.description,
|
|
315
|
+
metadata: response1.session?.metadata,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
console.log("💾 Session saved to database");
|
|
320
|
+
|
|
321
|
+
// Update session for next turn
|
|
322
|
+
agentSession = response1.session!;
|
|
323
|
+
|
|
324
|
+
// Turn 2: User provides company
|
|
325
|
+
console.log("\n--- Turn 2 ---");
|
|
326
|
+
history.push(
|
|
327
|
+
createMessageEvent(EventSource.AI_AGENT, "Agent", response1.message)
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const userMessage2 = createMessageEvent(
|
|
331
|
+
EventSource.CUSTOMER,
|
|
332
|
+
"User",
|
|
333
|
+
"I work for Acme Corporation"
|
|
334
|
+
);
|
|
335
|
+
history.push(userMessage2);
|
|
336
|
+
|
|
337
|
+
await db.createMessage({
|
|
338
|
+
sessionId: dbSession.id,
|
|
339
|
+
userId,
|
|
340
|
+
role: "user",
|
|
341
|
+
content: userMessage2.data.message,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const response2 = await agent.respond({
|
|
345
|
+
history,
|
|
346
|
+
session: agentSession,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
console.log("🤖 Agent:", response2.message);
|
|
350
|
+
console.log("📊 Extracted so far:", response2.session?.extracted);
|
|
351
|
+
|
|
352
|
+
await db.createMessage({
|
|
353
|
+
sessionId: dbSession.id,
|
|
354
|
+
userId,
|
|
355
|
+
role: "agent",
|
|
356
|
+
content: response2.message,
|
|
357
|
+
route: response2.session?.currentRoute?.id,
|
|
358
|
+
state: response2.session?.currentState?.id,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Save session state
|
|
362
|
+
await db.updateSession(dbSession.id, {
|
|
363
|
+
currentRoute: response2.session?.currentRoute?.id,
|
|
364
|
+
currentState: response2.session?.currentState?.id,
|
|
365
|
+
collectedData: {
|
|
366
|
+
extracted: response2.session?.extracted,
|
|
367
|
+
routeHistory: response2.session?.routeHistory,
|
|
368
|
+
currentRouteTitle: response2.session?.currentRoute?.title,
|
|
369
|
+
currentStateDescription: response2.session?.currentState?.description,
|
|
370
|
+
metadata: response2.session?.metadata,
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
console.log("💾 Session saved to database");
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Demonstrate session recovery
|
|
378
|
+
*/
|
|
379
|
+
console.log("\n--- Session Recovery ---");
|
|
380
|
+
console.log("Simulating app restart...\n");
|
|
381
|
+
|
|
382
|
+
// Load session from database again
|
|
383
|
+
const reloadedDbSession = await db.findSession(dbSession.id);
|
|
384
|
+
if (!reloadedDbSession) throw new Error("Session not found");
|
|
385
|
+
|
|
386
|
+
// Reconstruct session state
|
|
387
|
+
const recoveredSession: SessionState<OnboardingData> = {
|
|
388
|
+
currentRoute: reloadedDbSession.currentRoute
|
|
389
|
+
? {
|
|
390
|
+
id: reloadedDbSession.currentRoute,
|
|
391
|
+
title:
|
|
392
|
+
reloadedDbSession.collectedData?.currentRouteTitle ||
|
|
393
|
+
reloadedDbSession.currentRoute,
|
|
394
|
+
enteredAt: new Date(),
|
|
395
|
+
}
|
|
396
|
+
: undefined,
|
|
397
|
+
currentState: reloadedDbSession.currentState
|
|
398
|
+
? {
|
|
399
|
+
id: reloadedDbSession.currentState,
|
|
400
|
+
description: reloadedDbSession.collectedData?.currentStateDescription,
|
|
401
|
+
enteredAt: new Date(),
|
|
402
|
+
}
|
|
403
|
+
: undefined,
|
|
404
|
+
extracted:
|
|
405
|
+
(reloadedDbSession.collectedData?.extracted as Partial<OnboardingData>) ||
|
|
406
|
+
{},
|
|
407
|
+
routeHistory:
|
|
408
|
+
(reloadedDbSession.collectedData
|
|
409
|
+
?.routeHistory as SessionState<OnboardingData>["routeHistory"]) || [],
|
|
410
|
+
metadata: {
|
|
411
|
+
sessionId: reloadedDbSession.id,
|
|
412
|
+
userId,
|
|
413
|
+
createdAt: reloadedDbSession.createdAt,
|
|
414
|
+
lastUpdatedAt: reloadedDbSession.updatedAt,
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
console.log("✅ Session recovered from database:", {
|
|
419
|
+
sessionId: recoveredSession.metadata?.sessionId,
|
|
420
|
+
currentRoute: recoveredSession.currentRoute?.title,
|
|
421
|
+
currentState: recoveredSession.currentState?.id,
|
|
422
|
+
extracted: recoveredSession.extracted,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Load message history
|
|
426
|
+
const messages = await db.getSessionMessages(dbSession.id);
|
|
427
|
+
console.log(`📜 Loaded ${messages.length} messages from history`);
|
|
428
|
+
|
|
429
|
+
console.log("\n✅ Example complete!");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Advanced Example: With validation hooks
|
|
434
|
+
*/
|
|
435
|
+
async function advancedExample() {
|
|
436
|
+
const db = new CustomDatabase();
|
|
437
|
+
const userId = "user_456";
|
|
438
|
+
|
|
439
|
+
const agent = new Agent({
|
|
440
|
+
name: "Smart Onboarding",
|
|
441
|
+
ai: new GeminiProvider({
|
|
442
|
+
apiKey: process.env.GEMINI_API_KEY!,
|
|
443
|
+
model: "models/gemini-2.0-flash-exp",
|
|
444
|
+
}),
|
|
445
|
+
hooks: {
|
|
446
|
+
// Validate and enrich extracted data
|
|
447
|
+
onExtractedUpdate: async (extracted, previous) => {
|
|
448
|
+
console.log("🔄 Data extracted, validating...");
|
|
449
|
+
|
|
450
|
+
// Normalize email
|
|
451
|
+
if (extracted.email) {
|
|
452
|
+
extracted.email = extracted.email.toLowerCase().trim();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Normalize phone
|
|
456
|
+
if (extracted.phoneNumber) {
|
|
457
|
+
extracted.phoneNumber = extracted.phoneNumber.replace(/\D/g, "");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return extracted;
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
const route = agent.createRoute<OnboardingData>({
|
|
466
|
+
title: "Onboarding",
|
|
467
|
+
gatherSchema: {
|
|
468
|
+
type: "object",
|
|
469
|
+
properties: {
|
|
470
|
+
fullName: { type: "string" },
|
|
471
|
+
email: { type: "string" },
|
|
472
|
+
companyName: { type: "string" },
|
|
473
|
+
phoneNumber: { type: "string" },
|
|
474
|
+
},
|
|
475
|
+
required: ["fullName", "email", "companyName"],
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
route.initialState.transitionTo({
|
|
480
|
+
id: "collect_all",
|
|
481
|
+
chatState: "Collect all information",
|
|
482
|
+
gather: ["fullName", "email", "companyName", "phoneNumber"],
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Create database session
|
|
486
|
+
const dbSession = await db.createSession({
|
|
487
|
+
userId,
|
|
488
|
+
collectedData: {},
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Create agent session
|
|
492
|
+
let agentSession = createSession<OnboardingData>(dbSession.id, {
|
|
493
|
+
userId,
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// Simulate conversation
|
|
497
|
+
const response = await agent.respond({
|
|
498
|
+
history: [
|
|
499
|
+
createMessageEvent(
|
|
500
|
+
EventSource.CUSTOMER,
|
|
501
|
+
"User",
|
|
502
|
+
"I'm Alice Johnson, alice@EXAMPLE.COM, working for TechCorp. Phone: (555) 123-4567"
|
|
503
|
+
),
|
|
504
|
+
],
|
|
505
|
+
session: agentSession,
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
console.log("🤖 Agent:", response.message);
|
|
509
|
+
console.log("📊 Normalized data:", response.session?.extracted);
|
|
510
|
+
// Shows: { email: "alice@example.com", phoneNumber: "5551234567", ... }
|
|
511
|
+
|
|
512
|
+
// Save to database
|
|
513
|
+
await db.updateSession(dbSession.id, {
|
|
514
|
+
currentRoute: response.session?.currentRoute?.id,
|
|
515
|
+
currentState: response.session?.currentState?.id,
|
|
516
|
+
collectedData: {
|
|
517
|
+
extracted: response.session?.extracted,
|
|
518
|
+
routeHistory: response.session?.routeHistory,
|
|
519
|
+
currentRouteTitle: response.session?.currentRoute?.title,
|
|
520
|
+
currentStateDescription: response.session?.currentState?.description,
|
|
521
|
+
metadata: response.session?.metadata,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
console.log("✅ Validated data saved to custom database!");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Run the example
|
|
529
|
+
if (require.main === module) {
|
|
530
|
+
example().catch(console.error);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export { example, advancedExample };
|
|
@@ -128,14 +128,16 @@ async function example() {
|
|
|
128
128
|
},
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
// State flow with smart data gathering
|
|
131
|
+
// State flow with smart data gathering and custom IDs
|
|
132
132
|
const askDestination = flightRoute.initialState.transitionTo({
|
|
133
|
+
id: "ask_destination", // Custom state ID for easier tracking
|
|
133
134
|
chatState: "Ask where they want to fly",
|
|
134
135
|
gather: ["destination"],
|
|
135
136
|
skipIf: (extracted) => !!extracted.destination,
|
|
136
137
|
});
|
|
137
138
|
|
|
138
139
|
const askDates = askDestination.transitionTo({
|
|
140
|
+
id: "ask_dates", // Custom state ID
|
|
139
141
|
chatState: "Ask about travel dates",
|
|
140
142
|
gather: ["departureDate", "returnDate"],
|
|
141
143
|
skipIf: (extracted) => !!extracted.departureDate,
|
|
@@ -143,6 +145,7 @@ async function example() {
|
|
|
143
145
|
});
|
|
144
146
|
|
|
145
147
|
const askPassengers = askDates.transitionTo({
|
|
148
|
+
id: "ask_passengers", // Custom state ID
|
|
146
149
|
chatState: "Ask how many passengers",
|
|
147
150
|
gather: ["passengers"],
|
|
148
151
|
skipIf: (extracted) => !!extracted.passengers,
|
|
@@ -150,6 +153,7 @@ async function example() {
|
|
|
150
153
|
});
|
|
151
154
|
|
|
152
155
|
const askCabinClass = askPassengers.transitionTo({
|
|
156
|
+
id: "ask_cabin_class", // Custom state ID
|
|
153
157
|
chatState: "Ask about cabin class preference",
|
|
154
158
|
gather: ["cabinClass"],
|
|
155
159
|
skipIf: (extracted) => !!extracted.cabinClass,
|
|
@@ -157,6 +161,7 @@ async function example() {
|
|
|
157
161
|
});
|
|
158
162
|
|
|
159
163
|
const confirmBooking = askCabinClass.transitionTo({
|
|
164
|
+
id: "confirm_booking", // Custom state ID
|
|
160
165
|
chatState: "Present options and confirm booking details",
|
|
161
166
|
requiredData: ["destination", "departureDate", "passengers", "cabinClass"],
|
|
162
167
|
});
|
|
@@ -186,6 +191,10 @@ async function example() {
|
|
|
186
191
|
const dbSessionId = sessionResult.sessionData.id;
|
|
187
192
|
|
|
188
193
|
console.log("✨ Created new session:", dbSessionId);
|
|
194
|
+
console.log("📊 Session metadata:", {
|
|
195
|
+
sessionId: session.metadata?.sessionId, // Same as dbSessionId
|
|
196
|
+
createdAt: session.metadata?.createdAt,
|
|
197
|
+
});
|
|
189
198
|
console.log("📊 Initial session state:", {
|
|
190
199
|
currentRoute: session.currentRoute,
|
|
191
200
|
extracted: session.extracted,
|
|
@@ -216,8 +225,10 @@ async function example() {
|
|
|
216
225
|
|
|
217
226
|
console.log("🤖 Agent:", response1.message);
|
|
218
227
|
console.log("📊 Session state after turn 1:", {
|
|
228
|
+
sessionId: response1.session?.metadata?.sessionId,
|
|
219
229
|
currentRoute: response1.session?.currentRoute?.title,
|
|
220
|
-
|
|
230
|
+
currentStateId: response1.session?.currentState?.id, // Custom ID like "ask_destination"
|
|
231
|
+
currentStateDescription: response1.session?.currentState?.description,
|
|
221
232
|
extracted: response1.session?.extracted,
|
|
222
233
|
});
|
|
223
234
|
|
package/package.json
CHANGED
package/src/core/State.ts
CHANGED
|
@@ -83,7 +83,7 @@ export class State<TContext = unknown, TExtracted = unknown> {
|
|
|
83
83
|
const targetState = new State<TContext, TExtracted>(
|
|
84
84
|
this.routeId,
|
|
85
85
|
spec.chatState,
|
|
86
|
-
|
|
86
|
+
spec.id, // Use custom ID if provided
|
|
87
87
|
spec.gather,
|
|
88
88
|
spec.skipIf,
|
|
89
89
|
spec.requiredData
|
package/src/types/route.ts
CHANGED
|
@@ -70,6 +70,8 @@ export interface RouteOptions<TExtracted = unknown> {
|
|
|
70
70
|
* Specification for a state transition
|
|
71
71
|
*/
|
|
72
72
|
export interface TransitionSpec<TContext = unknown, TExtracted = unknown> {
|
|
73
|
+
/** Custom ID for this state (optional - will generate deterministic ID if not provided) */
|
|
74
|
+
id?: string;
|
|
73
75
|
/** Transition to a chat state with this description */
|
|
74
76
|
chatState?: string;
|
|
75
77
|
/** Transition to execute a tool */
|