@minded-ai/mindedjs 1.0.19 → 1.0.21
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.js +13 -8
- package/dist/agent.js.map +1 -1
- package/dist/checkpointer/checkpointSaverFactory.d.ts +7 -0
- package/dist/checkpointer/checkpointSaverFactory.js +30 -0
- package/dist/checkpointer/checkpointSaverFactory.js.map +1 -0
- package/dist/edges/createPromptRouter.js +1 -1
- package/dist/edges/createPromptRouter.js.map +1 -1
- package/dist/platform/mindedCheckpointSaver.js +55 -10
- package/dist/platform/mindedCheckpointSaver.js.map +1 -1
- package/dist/platform/mindedConnection.d.ts +6 -1
- package/dist/platform/mindedConnection.js +92 -42
- package/dist/platform/mindedConnection.js.map +1 -1
- package/dist/platform/mindedConnectionTypes.d.ts +2 -0
- package/dist/platform/mindedConnectionTypes.js.map +1 -1
- package/dist/platform/mindedRequest.js +1 -1
- package/dist/platform/mindedRequest.js.map +1 -1
- package/docs/api-reference/classes/index.Agent.html +2 -2
- package/docs/api-reference/enums/index.EdgeType.html +2 -2
- package/docs/api-reference/enums/index.NodeType.html +2 -2
- package/docs/api-reference/enums/index.TriggerType.html +2 -2
- package/docs/api-reference/enums/index.events.html +2 -2
- package/docs/api-reference/hierarchy.html +1 -1
- package/docs/api-reference/index.html +3 -3
- package/docs/api-reference/interfaces/index.AppToolNode.html +2 -2
- package/docs/api-reference/interfaces/index.AppTriggerNode.html +2 -2
- package/docs/api-reference/interfaces/index.Flow.html +2 -2
- package/docs/api-reference/interfaces/index.JunctionNode.html +2 -2
- package/docs/api-reference/interfaces/index.LogicalConditionEdge.html +2 -2
- package/docs/api-reference/interfaces/index.ManualTriggerNode.html +2 -2
- package/docs/api-reference/interfaces/index.PromptConditionEdge.html +2 -2
- package/docs/api-reference/interfaces/index.PromptNode.html +2 -2
- package/docs/api-reference/interfaces/index.StepForwardEdge.html +2 -2
- package/docs/api-reference/interfaces/index.Tool.html +2 -2
- package/docs/api-reference/interfaces/index.ToolNode.html +2 -2
- package/docs/api-reference/modules/index-1.html +1 -1
- package/docs/api-reference/modules/index.html +1 -1
- package/docs/api-reference/modules.html +1 -1
- package/docs/api-reference/types/index.Edge.html +1 -1
- package/docs/api-reference/types/index.Node.html +1 -1
- package/docs/api-reference/types/index.TriggerNode.html +1 -1
- package/docs/examples/order-refund-flow.md +1 -1
- package/docs/getting-started/installation.md +3 -3
- package/examples/orderRefundAgent/flows/orderRefundFlow.yaml +3 -3
- package/examples/orderRefundAgent/orderRefundAgent.ts +9 -3
- package/package.json +4 -2
- package/src/agent.ts +15 -9
- package/src/checkpointer/checkpointSaverFactory.ts +31 -0
- package/src/edges/createPromptRouter.ts +1 -1
- package/src/platform/mindedCheckpointSaver.ts +75 -18
- package/src/platform/mindedConnection.ts +99 -55
- package/src/platform/mindedConnectionTypes.ts +4 -5
- package/src/platform/mindedRequest.ts +2 -1
- package/test/checkpoint-saver/minded-checkpoint-saver-list.test.ts +138 -0
- package/test/checkpoint-saver/minded-checkpoint-saver.test.ts +99 -0
- package/dist/analytics.d.ts +0 -6
- package/dist/analytics.js +0 -19
- package/dist/analytics.js.map +0 -1
- package/dist/infrastructure.ts/mindedRequest.d.ts +0 -8
- package/dist/infrastructure.ts/mindedRequest.js +0 -22
- package/dist/infrastructure.ts/mindedRequest.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>TriggerNode | @
|
|
1
|
+
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>TriggerNode | @minded-ai/mindedjs</title><meta name="description" content="Documentation for @minded-ai/mindedjs"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@minded-ai/mindedjs</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="../modules/index.html">index</a></li><li><a href="" aria-current="page">TriggerNode</a></li></ul><h1>Type Alias TriggerNode</h1></div><div class="tsd-signature"><span class="tsd-kind-type-alias">TriggerNode</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/index.AppTriggerNode.html" class="tsd-signature-type tsd-kind-interface">AppTriggerNode</a> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">WebhookTriggerNode</span> <span class="tsd-signature-symbol">|</span> <a href="../interfaces/index.ManualTriggerNode.html" class="tsd-signature-type tsd-kind-interface">ManualTriggerNode</a></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/agentsforce/mindedjs/blob/d3db8e73a98eb5a0ea8a43ace8e2bd5ceec39e5e/src/types/Flows.types.ts#L56">src/types/Flows.types.ts:56</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-external" name="external"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>External</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@minded-ai/mindedjs</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
|
|
@@ -12,19 +12,19 @@ Get MindedJS up and running in your project.
|
|
|
12
12
|
{% tabs %}
|
|
13
13
|
{% tab title="npm" %}
|
|
14
14
|
```bash
|
|
15
|
-
npm i @
|
|
15
|
+
npm i @minded-ai/mindedjs langchain
|
|
16
16
|
```
|
|
17
17
|
{% endtab %}
|
|
18
18
|
|
|
19
19
|
{% tab title="yarn" %}
|
|
20
20
|
```bash
|
|
21
|
-
yarn add @
|
|
21
|
+
yarn add @minded-ai/mindedjs langchain
|
|
22
22
|
```
|
|
23
23
|
{% endtab %}
|
|
24
24
|
|
|
25
25
|
{% tab title="pnpm" %}
|
|
26
26
|
```bash
|
|
27
|
-
pnpm add @
|
|
27
|
+
pnpm add @minded-ai/mindedjs langchain
|
|
28
28
|
```
|
|
29
29
|
{% endtab %}
|
|
30
30
|
{% endtabs %}
|
|
@@ -2,7 +2,7 @@ name: 'Order refund flow'
|
|
|
2
2
|
nodes:
|
|
3
3
|
- type: 'trigger'
|
|
4
4
|
triggerType: 'manual'
|
|
5
|
-
name: '
|
|
5
|
+
name: 'New Direct Message (Instant)'
|
|
6
6
|
- type: 'tool'
|
|
7
7
|
name: 'Refund order'
|
|
8
8
|
toolName: 'refundOrder'
|
|
@@ -17,9 +17,9 @@ nodes:
|
|
|
17
17
|
llmConfig:
|
|
18
18
|
name: 'ChatOpenAI'
|
|
19
19
|
properties:
|
|
20
|
-
model: '
|
|
20
|
+
model: 'gpt-4o'
|
|
21
21
|
edges:
|
|
22
|
-
- source: '
|
|
22
|
+
- source: 'New Direct Message (Instant)'
|
|
23
23
|
target: 'Prompt'
|
|
24
24
|
type: 'stepForward'
|
|
25
25
|
- source: 'Prompt'
|
|
@@ -10,6 +10,7 @@ const agent = new Agent({
|
|
|
10
10
|
memorySchema,
|
|
11
11
|
config: mindedConfig,
|
|
12
12
|
tools,
|
|
13
|
+
platformToken: '123',
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
agent.on(events.AI_MESSAGE, async ({ message }) => {
|
|
@@ -17,12 +18,17 @@ agent.on(events.AI_MESSAGE, async ({ message }) => {
|
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
agent.on(events.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
20
|
-
if (triggerName
|
|
21
|
+
if (triggerName == 'onMessage') {
|
|
22
|
+
const text = triggerBody?.content;
|
|
21
23
|
return {
|
|
22
24
|
memory: {},
|
|
23
|
-
messages: [new HumanMessage(
|
|
25
|
+
messages: [new HumanMessage({ content: text, response_metadata: {} })],
|
|
24
26
|
};
|
|
25
27
|
}
|
|
28
|
+
return {
|
|
29
|
+
memory: {},
|
|
30
|
+
messages: [new HumanMessage({ content: triggerBody?.event?.text || triggerBody?.text || 'Empty message', response_metadata: {} })],
|
|
31
|
+
};
|
|
26
32
|
});
|
|
27
33
|
|
|
28
34
|
const sendMessage = async (message: string) => {
|
|
@@ -49,7 +55,7 @@ if (process.env.NO_PLATFORM_CONNECT === 'true') {
|
|
|
49
55
|
|
|
50
56
|
await agent.invoke({
|
|
51
57
|
triggerBody: userInput,
|
|
52
|
-
triggerName: '
|
|
58
|
+
triggerName: 'New Direct Message (Instant)',
|
|
53
59
|
sessionId: 'cli-session',
|
|
54
60
|
});
|
|
55
61
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minded-ai/mindedjs",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "1.0.21",
|
|
4
|
+
"description": "MindedJS is a TypeScript library for building agents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -21,12 +21,14 @@
|
|
|
21
21
|
"@types/chai": "^4.3.11",
|
|
22
22
|
"@types/mocha": "^10.0.6",
|
|
23
23
|
"@types/node": "^20.11.19",
|
|
24
|
+
"@types/sinon": "^17.0.4",
|
|
24
25
|
"chai": "^4.3.10",
|
|
25
26
|
"dotenv": "^16.4.5",
|
|
26
27
|
"eslint": "^9.27.0",
|
|
27
28
|
"globals": "^16.2.0",
|
|
28
29
|
"mocha": "^10.3.0",
|
|
29
30
|
"nodemon": "^3.1.10",
|
|
31
|
+
"sinon": "^20.0.0",
|
|
30
32
|
"ts-node": "^10.9.2",
|
|
31
33
|
"typedoc": "^0.28.5",
|
|
32
34
|
"typescript": "^5.3.3",
|
package/src/agent.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Tool } from './types/Tools.types';
|
|
|
3
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
4
|
import { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
5
5
|
import { Command, StateGraph } from '@langchain/langgraph';
|
|
6
|
-
import {
|
|
6
|
+
import { BaseCheckpointSaver } from '@langchain/langgraph';
|
|
7
7
|
import { nodeFactory } from './nodes/nodeFactory';
|
|
8
8
|
import { CompiledGraph, PreCompiledGraph, stateAnnotation } from './types/LangGraph.types';
|
|
9
9
|
import { edgeFactory } from './edges/edgeFactory';
|
|
@@ -11,7 +11,7 @@ import { AgentEventRequestPayloads, AgentEventResponsePayloads, AgentEvents } fr
|
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
|
|
13
13
|
import { MindedConnection } from './platform/mindedConnection';
|
|
14
|
-
import { MindedConnectionSocketMessageType } from './platform/mindedConnectionTypes';
|
|
14
|
+
import { BaseMindedConnectionSocketMessage, MindedConnectionSocketMessageType, OnAppTrigger } from './platform/mindedConnectionTypes';
|
|
15
15
|
import * as fs from 'fs';
|
|
16
16
|
import * as path from 'path';
|
|
17
17
|
import * as yaml from 'js-yaml';
|
|
@@ -19,6 +19,7 @@ import { MindedSDKConfig } from './types/Agent.types';
|
|
|
19
19
|
import { createLlmInstance } from './llm/createLlmInstance';
|
|
20
20
|
import { config } from 'dotenv';
|
|
21
21
|
import { resolve } from 'path';
|
|
22
|
+
import { createCheckpointSaver } from './checkpointer/checkpointSaverFactory';
|
|
22
23
|
|
|
23
24
|
config({ path: resolve(__dirname, '../.env') });
|
|
24
25
|
|
|
@@ -55,15 +56,20 @@ export class Agent {
|
|
|
55
56
|
this.tools = tools;
|
|
56
57
|
this.platformToken = platformToken;
|
|
57
58
|
this.llm = createLlmInstance(config.llm);
|
|
58
|
-
this.checkpointer = memorySaver ||
|
|
59
|
+
this.checkpointer = memorySaver || createCheckpointSaver('');
|
|
59
60
|
this.validate();
|
|
60
61
|
this.compiledGraph = this.initializeGraph();
|
|
61
62
|
|
|
62
63
|
if (process.env.NODE_ENV !== 'test' && process.env.NO_PLATFORM_CONNECT !== 'true') {
|
|
63
64
|
this.mindedConnection = new MindedConnection();
|
|
64
65
|
this.mindedConnection.start();
|
|
65
|
-
this.mindedConnection.on(MindedConnectionSocketMessageType.OnAppTrigger, (
|
|
66
|
-
|
|
66
|
+
this.mindedConnection.on(MindedConnectionSocketMessageType.OnAppTrigger, async (message: BaseMindedConnectionSocketMessage) => {
|
|
67
|
+
const trigger = message as OnAppTrigger;
|
|
68
|
+
await this.invoke({
|
|
69
|
+
triggerBody: trigger.body,
|
|
70
|
+
triggerName: trigger.name,
|
|
71
|
+
sessionId: trigger.cnvId,
|
|
72
|
+
});
|
|
67
73
|
});
|
|
68
74
|
}
|
|
69
75
|
}
|
|
@@ -168,10 +174,10 @@ export class Agent {
|
|
|
168
174
|
const messages = handlerResult?.messages ?? [];
|
|
169
175
|
console.log(`Invoking trigger ${triggerName} with session ${session}`);
|
|
170
176
|
const config = { configurable: { thread_id: session, recursionLimit: 3 } };
|
|
171
|
-
const triggerNode = this.compiledGraph.builder.nodes[triggerName];
|
|
172
|
-
if (!triggerNode) {
|
|
173
|
-
|
|
174
|
-
}
|
|
177
|
+
// const triggerNode = this.compiledGraph.builder.nodes[triggerName];
|
|
178
|
+
// if (!triggerNode) {
|
|
179
|
+
// throw new Error(`Trigger node not found: ${triggerName}`);
|
|
180
|
+
// }
|
|
175
181
|
const state = await this.compiledGraph.getState(config);
|
|
176
182
|
// Resume interruption
|
|
177
183
|
if (state.tasks?.[0]?.interrupts?.length > 0) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { BaseCheckpointSaver, MemorySaver } from '@langchain/langgraph';
|
|
2
|
+
import { MindedCheckpointSaver } from '../platform/mindedCheckpointSaver';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Factory function to create the appropriate checkpoint saver based on environment variables
|
|
6
|
+
* @param platformToken - Optional platform token for remote checkpoint saving
|
|
7
|
+
* @returns BaseCheckpointSaver instance (either MemorySaver or MindedCheckpointSaver)
|
|
8
|
+
*/
|
|
9
|
+
export function createCheckpointSaver(platformToken: string): BaseCheckpointSaver {
|
|
10
|
+
const useRemoteCheckpointSaver = process.env.REMOTE_CHECKPOINT_SAVER === 'true';
|
|
11
|
+
|
|
12
|
+
if (!useRemoteCheckpointSaver || process.env.NODE_ENV === 'test') {
|
|
13
|
+
console.log('Using in-memory checkpoint saver');
|
|
14
|
+
return new MemorySaver();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// // If not using in-memory, check for platform token and MINDED_SERVER
|
|
18
|
+
// if (!platformToken) {
|
|
19
|
+
// console.warn('No platform token provided, falling back to in-memory checkpoint saver');
|
|
20
|
+
// return new MemorySaver();
|
|
21
|
+
// }
|
|
22
|
+
|
|
23
|
+
const mindedServer = process.env.MINDED_SERVER;
|
|
24
|
+
if (!mindedServer) {
|
|
25
|
+
console.warn('MINDED_SERVER environment variable not set, falling back to in-memory checkpoint saver');
|
|
26
|
+
return new MemorySaver();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(`Using remote checkpoint saver with server: ${mindedServer}`);
|
|
30
|
+
return new MindedCheckpointSaver(platformToken);
|
|
31
|
+
}
|
|
@@ -25,7 +25,7 @@ Your task is to analyze the current memory state, history and available nodes an
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
Available nodes in the format of {nodeId}: {prompt}:
|
|
28
|
-
${edges.map((edge) => `- ${edge.target}: ${edge.prompt}`).join('\n')}
|
|
28
|
+
${edges.map((edge) => `- {${edge.target}}: {${edge.prompt}}`).join('\n')}
|
|
29
29
|
|
|
30
30
|
Current memory state:
|
|
31
31
|
${JSON.stringify(state.memory)}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ChannelVersions,
|
|
7
7
|
PendingWrite,
|
|
8
8
|
CheckpointListOptions,
|
|
9
|
+
CheckpointPendingWrite,
|
|
9
10
|
} from '@langchain/langgraph-checkpoint';
|
|
10
11
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
11
12
|
import { mindedRequest } from './mindedRequest';
|
|
@@ -16,26 +17,82 @@ export class MindedCheckpointSaver extends BaseCheckpointSaver<number> {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
async getTuple(config: RunnableConfig<Record<string, any>>): Promise<CheckpointTuple | undefined> {
|
|
19
|
-
|
|
20
|
-
path: '/
|
|
20
|
+
const response = await mindedRequest<{ tuple: CheckpointTuple | undefined }, { config: RunnableConfig<Record<string, any>> }>({
|
|
21
|
+
path: '/sdk-checkpoints/getTuple',
|
|
21
22
|
method: 'POST',
|
|
22
23
|
body: { config },
|
|
23
24
|
token: this.token,
|
|
24
25
|
});
|
|
26
|
+
|
|
27
|
+
if (!response.tuple) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const tuple = response.tuple;
|
|
32
|
+
|
|
33
|
+
// Re-serialize and deserialize checkpoint to restore class information
|
|
34
|
+
const [checkpointType, serializedCheckpoint] = this.serde.dumpsTyped(tuple.checkpoint);
|
|
35
|
+
const checkpoint = (await this.serde.loadsTyped(checkpointType, serializedCheckpoint)) as Checkpoint;
|
|
36
|
+
|
|
37
|
+
// Re-serialize and deserialize metadata to restore class information
|
|
38
|
+
const metadata = tuple.metadata
|
|
39
|
+
? (() => {
|
|
40
|
+
const [metadataType, serializedMetadata] = this.serde.dumpsTyped(tuple.metadata);
|
|
41
|
+
return this.serde.loadsTyped(metadataType, serializedMetadata) as CheckpointMetadata;
|
|
42
|
+
})()
|
|
43
|
+
: undefined;
|
|
44
|
+
|
|
45
|
+
// Re-serialize and deserialize pending writes to restore class information
|
|
46
|
+
const pendingWrites: CheckpointPendingWrite[] = tuple.pendingWrites
|
|
47
|
+
? await Promise.all(
|
|
48
|
+
tuple.pendingWrites.map(async (write) => {
|
|
49
|
+
const [taskId, channel, value] = write;
|
|
50
|
+
const [valueType, serializedValue] = this.serde.dumpsTyped(value);
|
|
51
|
+
const deserializedValue = await this.serde.loadsTyped(valueType, serializedValue);
|
|
52
|
+
return [taskId, channel, deserializedValue] as CheckpointPendingWrite;
|
|
53
|
+
}),
|
|
54
|
+
)
|
|
55
|
+
: [];
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
config: tuple.config,
|
|
59
|
+
checkpoint,
|
|
60
|
+
metadata: await metadata,
|
|
61
|
+
parentConfig: tuple.parentConfig,
|
|
62
|
+
pendingWrites,
|
|
63
|
+
};
|
|
25
64
|
}
|
|
26
65
|
|
|
27
66
|
async *list(config: RunnableConfig<Record<string, any>>, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple, any, any> {
|
|
28
|
-
const
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
|
|
67
|
+
const response = await mindedRequest<
|
|
68
|
+
{ checkpoints: CheckpointTuple[] },
|
|
69
|
+
{ config: RunnableConfig<Record<string, any>>; options?: CheckpointListOptions }
|
|
70
|
+
>({
|
|
71
|
+
path: '/sdk-checkpoints/list',
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: { config, options },
|
|
74
|
+
token: this.token,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
for (const tuple of response.checkpoints) {
|
|
78
|
+
// Re-serialize and deserialize checkpoint to restore class information
|
|
79
|
+
const [checkpointType, serializedCheckpoint] = this.serde.dumpsTyped(tuple.checkpoint);
|
|
80
|
+
const checkpoint = (await this.serde.loadsTyped(checkpointType, serializedCheckpoint)) as Checkpoint;
|
|
81
|
+
|
|
82
|
+
// Re-serialize and deserialize metadata to restore class information
|
|
83
|
+
const metadata = tuple.metadata
|
|
84
|
+
? (() => {
|
|
85
|
+
const [metadataType, serializedMetadata] = this.serde.dumpsTyped(tuple.metadata);
|
|
86
|
+
return this.serde.loadsTyped(metadataType, serializedMetadata) as CheckpointMetadata;
|
|
87
|
+
})()
|
|
88
|
+
: undefined;
|
|
36
89
|
|
|
37
|
-
|
|
38
|
-
|
|
90
|
+
yield {
|
|
91
|
+
config: tuple.config,
|
|
92
|
+
checkpoint,
|
|
93
|
+
metadata: await metadata,
|
|
94
|
+
parentConfig: tuple.parentConfig,
|
|
95
|
+
};
|
|
39
96
|
}
|
|
40
97
|
}
|
|
41
98
|
|
|
@@ -45,8 +102,8 @@ export class MindedCheckpointSaver extends BaseCheckpointSaver<number> {
|
|
|
45
102
|
metadata: CheckpointMetadata,
|
|
46
103
|
newVersions: ChannelVersions,
|
|
47
104
|
): Promise<RunnableConfig<Record<string, any>>> {
|
|
48
|
-
const
|
|
49
|
-
RunnableConfig<Record<string, any
|
|
105
|
+
const response = await mindedRequest<
|
|
106
|
+
{ config: RunnableConfig<Record<string, any>> },
|
|
50
107
|
{
|
|
51
108
|
config: RunnableConfig<Record<string, any>>;
|
|
52
109
|
checkpoint: Checkpoint<string, string>;
|
|
@@ -54,18 +111,18 @@ export class MindedCheckpointSaver extends BaseCheckpointSaver<number> {
|
|
|
54
111
|
newVersions: ChannelVersions;
|
|
55
112
|
}
|
|
56
113
|
>({
|
|
57
|
-
path: '/
|
|
114
|
+
path: '/sdk-checkpoints/put',
|
|
58
115
|
method: 'POST',
|
|
59
116
|
body: { config, checkpoint, metadata, newVersions },
|
|
60
117
|
token: this.token,
|
|
61
118
|
});
|
|
62
119
|
|
|
63
|
-
return
|
|
120
|
+
return response.config;
|
|
64
121
|
}
|
|
65
122
|
|
|
66
123
|
async putWrites(config: RunnableConfig<Record<string, any>>, writes: PendingWrite[], taskId: string): Promise<void> {
|
|
67
|
-
await mindedRequest
|
|
68
|
-
path: '/
|
|
124
|
+
await mindedRequest({
|
|
125
|
+
path: '/sdk-checkpoints/putWrites',
|
|
69
126
|
method: 'POST',
|
|
70
127
|
body: { config, writes, taskId },
|
|
71
128
|
token: this.token,
|
|
@@ -1,106 +1,150 @@
|
|
|
1
1
|
import { io, Socket } from 'socket.io-client';
|
|
2
2
|
import * as readline from 'readline';
|
|
3
3
|
import { BaseMindedConnectionSocketMessage, MindedConnectionSocketMessageType } from './mindedConnectionTypes';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
4
6
|
|
|
5
7
|
export class MindedConnection {
|
|
6
8
|
private socket: Socket | null = null;
|
|
7
9
|
private rl: readline.Interface | null = null;
|
|
10
|
+
private readonly tokenPath = path.join(process.cwd(), '.minded-token');
|
|
8
11
|
listeners: {
|
|
9
|
-
[key: string]: ((message: BaseMindedConnectionSocketMessage) => void)[]
|
|
12
|
+
[key: string]: ((message: BaseMindedConnectionSocketMessage) => void)[];
|
|
10
13
|
} = {};
|
|
11
14
|
|
|
12
15
|
constructor() {
|
|
13
|
-
if (!process.env.MINDED_CONNECTION_TOKEN) {
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if (!process.env.MINDED_CONNECTION_TOKEN && !this.getSavedToken()) {
|
|
17
|
+
this.initializeReadline();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private initializeReadline() {
|
|
22
|
+
this.rl = readline.createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private getSavedToken(): string | null {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(this.tokenPath)) {
|
|
31
|
+
return fs.readFileSync(this.tokenPath, 'utf8').trim();
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Error reading token file:', error);
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private saveToken(token: string): void {
|
|
40
|
+
try {
|
|
41
|
+
fs.writeFileSync(this.tokenPath, token);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('Error saving token:', error);
|
|
18
44
|
}
|
|
19
45
|
}
|
|
20
46
|
|
|
21
47
|
private async getMindedToken(): Promise<string> {
|
|
22
48
|
const envToken = process.env.MINDED_CONNECTION_TOKEN;
|
|
23
|
-
|
|
24
49
|
if (envToken) {
|
|
50
|
+
this.saveToken(envToken);
|
|
25
51
|
return envToken;
|
|
26
52
|
}
|
|
27
53
|
|
|
54
|
+
const savedToken = this.getSavedToken();
|
|
55
|
+
if (savedToken) {
|
|
56
|
+
return savedToken;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!this.rl) {
|
|
60
|
+
this.initializeReadline();
|
|
61
|
+
}
|
|
62
|
+
|
|
28
63
|
return new Promise((resolve) => {
|
|
29
64
|
this.rl!.question('Enter your Minded connection token: ', (token: string) => {
|
|
30
|
-
|
|
65
|
+
this.saveToken(token.trim());
|
|
66
|
+
resolve(token.trim());
|
|
31
67
|
});
|
|
32
68
|
});
|
|
33
69
|
}
|
|
34
70
|
|
|
35
|
-
public on = (
|
|
36
|
-
event: MindedConnectionSocketMessageType,
|
|
37
|
-
callback: (message: BaseMindedConnectionSocketMessage) => void,
|
|
38
|
-
) => {
|
|
71
|
+
public on = (event: MindedConnectionSocketMessageType, callback: (message: BaseMindedConnectionSocketMessage) => void) => {
|
|
39
72
|
if (!this.listeners[event]) {
|
|
40
73
|
this.listeners[event] = [];
|
|
41
74
|
}
|
|
42
75
|
this.listeners[event].push(callback);
|
|
43
76
|
};
|
|
44
77
|
|
|
45
|
-
|
|
46
|
-
public emit = (
|
|
47
|
-
event: MindedConnectionSocketMessageType,
|
|
48
|
-
message: BaseMindedConnectionSocketMessage,
|
|
49
|
-
) => {
|
|
78
|
+
public emit = (event: MindedConnectionSocketMessageType, message: BaseMindedConnectionSocketMessage) => {
|
|
50
79
|
if (this.socket) {
|
|
51
80
|
this.socket.emit(event, message);
|
|
52
81
|
}
|
|
53
82
|
};
|
|
54
83
|
|
|
84
|
+
private async connect(token: string) {
|
|
85
|
+
this.socket = io('http://localhost:8888', {
|
|
86
|
+
path: '/minded-connect',
|
|
87
|
+
query: {
|
|
88
|
+
token,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
55
91
|
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
token
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Connection event handlers
|
|
66
|
-
this.socket.on('connect', () => {
|
|
67
|
-
console.log('\x1b[32mConnection with Minded platform is live!\x1b[0m');
|
|
68
|
-
console.log('\x1b[36mPress Ctrl+C to exit.\x1b[0m');
|
|
69
|
-
});
|
|
92
|
+
// Connection event handlers
|
|
93
|
+
this.socket.on('connect', () => {
|
|
94
|
+
console.log('\x1b[32mConnection with Minded platform is live!\x1b[0m');
|
|
95
|
+
console.log('\x1b[36mPress Ctrl+C to exit.\x1b[0m');
|
|
96
|
+
});
|
|
70
97
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
this.socket.on('connect_error', () => {
|
|
99
|
+
console.error('Failed to connect to minded platform');
|
|
100
|
+
});
|
|
74
101
|
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
this.socket.on('disconnect', () => {
|
|
103
|
+
console.log('Disconnected from local debugging socket');
|
|
104
|
+
});
|
|
77
105
|
|
|
78
|
-
|
|
106
|
+
// Listen for error messages from the server
|
|
107
|
+
this.socket.on('error', async (error: { message: string }) => {
|
|
108
|
+
console.error('Server error:', error.message);
|
|
79
109
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// Listen for specific message types
|
|
86
|
-
this.socket.onAny((event, message) => {
|
|
87
|
-
if (this.listeners[event]) {
|
|
88
|
-
this.listeners[event].forEach((listener) => {
|
|
89
|
-
listener(message);
|
|
90
|
-
});
|
|
110
|
+
if (error.message.includes('Invalid token')) {
|
|
111
|
+
// Delete saved token on connection error
|
|
112
|
+
if (fs.existsSync(this.tokenPath)) {
|
|
113
|
+
fs.unlinkSync(this.tokenPath);
|
|
91
114
|
}
|
|
92
|
-
});
|
|
93
115
|
|
|
94
|
-
|
|
95
|
-
process.on('SIGINT', () => {
|
|
116
|
+
// Disconnect current socket
|
|
96
117
|
if (this.socket?.connected) {
|
|
97
|
-
console.log('\nDisconnecting...');
|
|
98
118
|
this.socket.disconnect();
|
|
99
119
|
}
|
|
100
|
-
process.exit(0);
|
|
101
|
-
});
|
|
102
120
|
|
|
121
|
+
// Get new token and reconnect
|
|
122
|
+
const newToken = await this.getMindedToken();
|
|
123
|
+
await this.connect(newToken);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Listen for specific message types
|
|
128
|
+
this.socket.onAny((event, message) => {
|
|
129
|
+
if (this.listeners[event]) {
|
|
130
|
+
this.listeners[event].forEach((listener) => {
|
|
131
|
+
listener(message);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Handle process termination
|
|
137
|
+
process.on('SIGINT', () => {
|
|
138
|
+
if (this.socket?.connected) {
|
|
139
|
+
console.log('\nDisconnecting...');
|
|
140
|
+
this.socket.disconnect();
|
|
141
|
+
}
|
|
142
|
+
process.exit(0);
|
|
103
143
|
});
|
|
144
|
+
}
|
|
104
145
|
|
|
146
|
+
public async start(): Promise<void> {
|
|
147
|
+
const token = await this.getMindedToken();
|
|
148
|
+
await this.connect(token);
|
|
105
149
|
}
|
|
106
|
-
}
|
|
150
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export enum MindedConnectionSocketMessageType {
|
|
1
|
+
export enum MindedConnectionSocketMessageType {
|
|
3
2
|
OnAppTrigger = 'on-app-trigger',
|
|
4
3
|
}
|
|
5
4
|
|
|
@@ -7,9 +6,9 @@ export interface BaseMindedConnectionSocketMessage {
|
|
|
7
6
|
type: MindedConnectionSocketMessageType;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
export interface OnAppTrigger extends BaseMindedConnectionSocketMessage {
|
|
9
|
+
export interface OnAppTrigger extends BaseMindedConnectionSocketMessage {
|
|
11
10
|
name: string;
|
|
11
|
+
agentId: string;
|
|
12
12
|
body: Record<string, any>;
|
|
13
|
+
cnvId: string;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
const baseUrl = 'https://api.minded.com/sdk';
|
|
3
2
|
|
|
4
3
|
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
5
4
|
|
|
@@ -14,6 +13,8 @@ export const mindedRequest = async <Response, Request = object>({
|
|
|
14
13
|
body: Request;
|
|
15
14
|
token: string;
|
|
16
15
|
}): Promise<Response> => {
|
|
16
|
+
const baseUrl = process.env.MINDED_SERVER || 'https://api.minded.com/sdk';
|
|
17
|
+
|
|
17
18
|
const response = await axios({
|
|
18
19
|
method,
|
|
19
20
|
url: `${baseUrl}${path}`,
|