@lota-sdk/core 0.1.8 → 0.1.11
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/infrastructure/schema/00_workstream.surql +2 -1
- package/infrastructure/schema/02_execution_plan.surql +202 -52
- package/package.json +4 -2
- package/src/bifrost/bifrost.ts +94 -25
- package/src/config/model-constants.ts +8 -6
- package/src/db/memory-store.ts +3 -71
- package/src/db/service.ts +42 -2
- package/src/db/tables.ts +9 -2
- package/src/embeddings/provider.ts +92 -21
- package/src/index.ts +6 -0
- package/src/redis/stream-context.ts +44 -0
- package/src/runtime/approval-continuation.ts +59 -0
- package/src/runtime/chat-request-routing.ts +5 -1
- package/src/runtime/execution-plan.ts +21 -14
- package/src/runtime/turn-lifecycle.ts +14 -6
- package/src/runtime/workstream-chat-helpers.ts +5 -5
- package/src/services/context-compaction.service.ts +6 -2
- package/src/services/document-chunk.service.ts +2 -2
- package/src/services/execution-plan.service.ts +579 -786
- package/src/services/learned-skill.service.ts +2 -2
- package/src/services/plan-approval.service.ts +83 -0
- package/src/services/plan-artifact.service.ts +45 -0
- package/src/services/plan-builder.service.ts +61 -0
- package/src/services/plan-checkpoint.service.ts +53 -0
- package/src/services/plan-compiler.service.ts +81 -0
- package/src/services/plan-executor.service.ts +1623 -0
- package/src/services/plan-run.service.ts +422 -0
- package/src/services/plan-validator.service.ts +760 -0
- package/src/services/workstream-turn-preparation.ts +70 -196
- package/src/services/workstream-turn.ts +12 -0
- package/src/services/workstream.service.ts +24 -182
- package/src/services/workstream.types.ts +2 -65
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +20 -46
- package/src/tools/log-hello-world.tool.ts +17 -0
- package/src/workers/skill-extraction.runner.ts +2 -2
- package/src/services/workstream-change-tracker.service.ts +0 -313
- package/src/system-agents/workstream-tracker.agent.ts +0 -58
|
@@ -13,7 +13,8 @@ DEFINE FIELD IF NOT EXISTS updatedAt ON TABLE workstream TYPE datetime VALUE tim
|
|
|
13
13
|
DEFINE FIELD IF NOT EXISTS memoryBlock ON TABLE workstream TYPE option<string>;
|
|
14
14
|
DEFINE FIELD IF NOT EXISTS memoryBlockSummary ON TABLE workstream TYPE option<string>;
|
|
15
15
|
DEFINE FIELD IF NOT EXISTS activeRunId ON TABLE workstream TYPE option<string>;
|
|
16
|
-
DEFINE FIELD IF NOT EXISTS
|
|
16
|
+
DEFINE FIELD IF NOT EXISTS activeStreamId ON TABLE workstream TYPE option<string>;
|
|
17
|
+
DEFINE FIELD IF NOT EXISTS compactionSummary ON TABLE workstream TYPE option<string>;
|
|
17
18
|
DEFINE FIELD IF NOT EXISTS lastCompactedMessageId ON TABLE workstream TYPE option<string>;
|
|
18
19
|
DEFINE FIELD IF NOT EXISTS nameGenerated ON TABLE workstream TYPE bool DEFAULT false;
|
|
19
20
|
DEFINE FIELD IF NOT EXISTS isCompacting ON TABLE workstream TYPE bool DEFAULT false;
|
|
@@ -1,53 +1,204 @@
|
|
|
1
|
-
# Execution plan runtime.
|
|
2
|
-
|
|
3
|
-
DEFINE
|
|
4
|
-
DEFINE FIELD IF NOT EXISTS
|
|
5
|
-
DEFINE FIELD IF NOT EXISTS
|
|
6
|
-
DEFINE FIELD IF NOT EXISTS
|
|
7
|
-
DEFINE FIELD IF NOT EXISTS
|
|
8
|
-
DEFINE FIELD IF NOT EXISTS
|
|
9
|
-
DEFINE FIELD IF NOT EXISTS
|
|
10
|
-
DEFINE FIELD IF NOT EXISTS
|
|
11
|
-
DEFINE FIELD IF NOT EXISTS
|
|
12
|
-
DEFINE FIELD IF NOT EXISTS
|
|
13
|
-
DEFINE FIELD IF NOT EXISTS
|
|
14
|
-
DEFINE FIELD IF NOT EXISTS
|
|
15
|
-
DEFINE FIELD IF NOT EXISTS
|
|
16
|
-
DEFINE FIELD IF NOT EXISTS
|
|
17
|
-
|
|
18
|
-
DEFINE
|
|
19
|
-
DEFINE
|
|
20
|
-
|
|
21
|
-
DEFINE
|
|
22
|
-
DEFINE FIELD IF NOT EXISTS
|
|
23
|
-
DEFINE FIELD IF NOT EXISTS
|
|
24
|
-
|
|
25
|
-
DEFINE
|
|
26
|
-
DEFINE
|
|
27
|
-
|
|
28
|
-
DEFINE
|
|
29
|
-
DEFINE FIELD IF NOT EXISTS
|
|
30
|
-
DEFINE FIELD IF NOT EXISTS
|
|
31
|
-
DEFINE FIELD IF NOT EXISTS
|
|
32
|
-
DEFINE FIELD IF NOT EXISTS
|
|
33
|
-
DEFINE FIELD IF NOT EXISTS
|
|
34
|
-
DEFINE FIELD IF NOT EXISTS
|
|
35
|
-
DEFINE FIELD IF NOT EXISTS
|
|
36
|
-
DEFINE FIELD IF NOT EXISTS
|
|
37
|
-
DEFINE FIELD IF NOT EXISTS
|
|
38
|
-
DEFINE FIELD IF NOT EXISTS
|
|
39
|
-
DEFINE FIELD IF NOT EXISTS
|
|
40
|
-
DEFINE FIELD IF NOT EXISTS
|
|
41
|
-
DEFINE FIELD IF NOT EXISTS
|
|
42
|
-
DEFINE FIELD IF NOT EXISTS
|
|
43
|
-
|
|
44
|
-
DEFINE
|
|
45
|
-
DEFINE
|
|
46
|
-
DEFINE
|
|
1
|
+
# Execution plan runtime v2.
|
|
2
|
+
|
|
3
|
+
DEFINE TABLE IF NOT EXISTS planSpec SCHEMAFULL;
|
|
4
|
+
DEFINE FIELD IF NOT EXISTS organizationId ON TABLE planSpec TYPE record<organization>;
|
|
5
|
+
DEFINE FIELD IF NOT EXISTS workstreamId ON TABLE planSpec TYPE record<workstream> REFERENCE ON DELETE CASCADE;
|
|
6
|
+
DEFINE FIELD IF NOT EXISTS title ON TABLE planSpec TYPE string;
|
|
7
|
+
DEFINE FIELD IF NOT EXISTS objective ON TABLE planSpec TYPE string;
|
|
8
|
+
DEFINE FIELD IF NOT EXISTS version ON TABLE planSpec TYPE int;
|
|
9
|
+
DEFINE FIELD IF NOT EXISTS status ON TABLE planSpec TYPE string;
|
|
10
|
+
DEFINE FIELD IF NOT EXISTS leadAgentId ON TABLE planSpec TYPE string;
|
|
11
|
+
DEFINE FIELD IF NOT EXISTS schemaRegistry ON TABLE planSpec TYPE object FLEXIBLE;
|
|
12
|
+
DEFINE FIELD IF NOT EXISTS edges ON TABLE planSpec TYPE array<object> FLEXIBLE DEFAULT [];
|
|
13
|
+
DEFINE FIELD IF NOT EXISTS edges.* ON TABLE planSpec TYPE object FLEXIBLE;
|
|
14
|
+
DEFINE FIELD IF NOT EXISTS edges.*.id ON TABLE planSpec TYPE string;
|
|
15
|
+
DEFINE FIELD IF NOT EXISTS edges.*.source ON TABLE planSpec TYPE string;
|
|
16
|
+
DEFINE FIELD IF NOT EXISTS edges.*.target ON TABLE planSpec TYPE string;
|
|
17
|
+
DEFINE FIELD IF NOT EXISTS edges.*.when ON TABLE planSpec TYPE option<string>;
|
|
18
|
+
DEFINE FIELD IF NOT EXISTS edges.*.map ON TABLE planSpec TYPE object FLEXIBLE DEFAULT {};
|
|
19
|
+
DEFINE FIELD IF NOT EXISTS entryNodeIds ON TABLE planSpec TYPE array<string>;
|
|
20
|
+
DEFINE FIELD IF NOT EXISTS replacedSpecId ON TABLE planSpec TYPE option<record<planSpec>>;
|
|
21
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planSpec TYPE datetime DEFAULT time::now() READONLY;
|
|
22
|
+
DEFINE FIELD IF NOT EXISTS updatedAt ON TABLE planSpec TYPE datetime VALUE time::now();
|
|
23
|
+
DEFINE FIELD IF NOT EXISTS compiledAt ON TABLE planSpec TYPE option<datetime>;
|
|
24
|
+
|
|
25
|
+
DEFINE INDEX IF NOT EXISTS planSpecWorkstreamIdx ON TABLE planSpec COLUMNS workstreamId;
|
|
26
|
+
DEFINE INDEX IF NOT EXISTS planSpecWorkstreamStatusIdx ON TABLE planSpec COLUMNS workstreamId, status;
|
|
27
|
+
|
|
28
|
+
DEFINE TABLE IF NOT EXISTS planNodeSpec SCHEMAFULL;
|
|
29
|
+
DEFINE FIELD IF NOT EXISTS planSpecId ON TABLE planNodeSpec TYPE record<planSpec> REFERENCE ON DELETE CASCADE;
|
|
30
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planNodeSpec TYPE string;
|
|
31
|
+
DEFINE FIELD IF NOT EXISTS position ON TABLE planNodeSpec TYPE int;
|
|
32
|
+
DEFINE FIELD IF NOT EXISTS type ON TABLE planNodeSpec TYPE string;
|
|
33
|
+
DEFINE FIELD IF NOT EXISTS label ON TABLE planNodeSpec TYPE string;
|
|
34
|
+
DEFINE FIELD IF NOT EXISTS owner ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
35
|
+
DEFINE FIELD IF NOT EXISTS objective ON TABLE planNodeSpec TYPE string;
|
|
36
|
+
DEFINE FIELD IF NOT EXISTS instructions ON TABLE planNodeSpec TYPE string;
|
|
37
|
+
DEFINE FIELD IF NOT EXISTS inputSchemaRef ON TABLE planNodeSpec TYPE option<string>;
|
|
38
|
+
DEFINE FIELD IF NOT EXISTS outputSchemaRef ON TABLE planNodeSpec TYPE option<string>;
|
|
39
|
+
DEFINE FIELD IF NOT EXISTS deliverables ON TABLE planNodeSpec TYPE array<object> FLEXIBLE DEFAULT [];
|
|
40
|
+
DEFINE FIELD IF NOT EXISTS deliverables.* ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
41
|
+
DEFINE FIELD IF NOT EXISTS deliverables.*.name ON TABLE planNodeSpec TYPE string;
|
|
42
|
+
DEFINE FIELD IF NOT EXISTS deliverables.*.kind ON TABLE planNodeSpec TYPE string;
|
|
43
|
+
DEFINE FIELD IF NOT EXISTS deliverables.*.required ON TABLE planNodeSpec TYPE bool DEFAULT true;
|
|
44
|
+
DEFINE FIELD IF NOT EXISTS deliverables.*.schemaRef ON TABLE planNodeSpec TYPE option<string>;
|
|
45
|
+
DEFINE FIELD IF NOT EXISTS deliverables.*.description ON TABLE planNodeSpec TYPE string;
|
|
46
|
+
DEFINE FIELD IF NOT EXISTS successCriteria ON TABLE planNodeSpec TYPE array<string> DEFAULT [];
|
|
47
|
+
DEFINE FIELD IF NOT EXISTS completionChecks ON TABLE planNodeSpec TYPE array<object> FLEXIBLE DEFAULT [];
|
|
48
|
+
DEFINE FIELD IF NOT EXISTS completionChecks.* ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
49
|
+
DEFINE FIELD IF NOT EXISTS completionChecks.*.type ON TABLE planNodeSpec TYPE string;
|
|
50
|
+
DEFINE FIELD IF NOT EXISTS completionChecks.*.config ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
51
|
+
DEFINE FIELD IF NOT EXISTS completionChecks.*.blocking ON TABLE planNodeSpec TYPE bool DEFAULT true;
|
|
52
|
+
DEFINE FIELD IF NOT EXISTS completionChecks.*.description ON TABLE planNodeSpec TYPE string;
|
|
53
|
+
DEFINE FIELD IF NOT EXISTS retryPolicy ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
54
|
+
DEFINE FIELD IF NOT EXISTS failurePolicy ON TABLE planNodeSpec TYPE array<object> FLEXIBLE DEFAULT [];
|
|
55
|
+
DEFINE FIELD IF NOT EXISTS failurePolicy.* ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
56
|
+
DEFINE FIELD IF NOT EXISTS failurePolicy.*.on ON TABLE planNodeSpec TYPE string;
|
|
57
|
+
DEFINE FIELD IF NOT EXISTS failurePolicy.*.action ON TABLE planNodeSpec TYPE string;
|
|
58
|
+
DEFINE FIELD IF NOT EXISTS failurePolicy.*.note ON TABLE planNodeSpec TYPE string;
|
|
59
|
+
DEFINE FIELD IF NOT EXISTS timeoutMs ON TABLE planNodeSpec TYPE option<int>;
|
|
60
|
+
DEFINE FIELD IF NOT EXISTS toolPolicy ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
61
|
+
DEFINE FIELD IF NOT EXISTS contextPolicy ON TABLE planNodeSpec TYPE object FLEXIBLE;
|
|
62
|
+
DEFINE FIELD IF NOT EXISTS upstreamNodeIds ON TABLE planNodeSpec TYPE array<string> DEFAULT [];
|
|
63
|
+
DEFINE FIELD IF NOT EXISTS downstreamNodeIds ON TABLE planNodeSpec TYPE array<string> DEFAULT [];
|
|
64
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planNodeSpec TYPE datetime DEFAULT time::now() READONLY;
|
|
65
|
+
DEFINE FIELD IF NOT EXISTS updatedAt ON TABLE planNodeSpec TYPE datetime VALUE time::now();
|
|
66
|
+
|
|
67
|
+
DEFINE INDEX IF NOT EXISTS planNodeSpecPlanIdx ON TABLE planNodeSpec COLUMNS planSpecId;
|
|
68
|
+
DEFINE INDEX IF NOT EXISTS planNodeSpecPlanNodeIdx ON TABLE planNodeSpec COLUMNS planSpecId, nodeId UNIQUE;
|
|
69
|
+
DEFINE INDEX IF NOT EXISTS planNodeSpecPlanPositionIdx ON TABLE planNodeSpec COLUMNS planSpecId, position;
|
|
70
|
+
|
|
71
|
+
DEFINE TABLE IF NOT EXISTS planRun SCHEMAFULL;
|
|
72
|
+
DEFINE FIELD IF NOT EXISTS planSpecId ON TABLE planRun TYPE record<planSpec> REFERENCE ON DELETE CASCADE;
|
|
73
|
+
DEFINE FIELD IF NOT EXISTS organizationId ON TABLE planRun TYPE record<organization>;
|
|
74
|
+
DEFINE FIELD IF NOT EXISTS workstreamId ON TABLE planRun TYPE record<workstream> REFERENCE ON DELETE CASCADE;
|
|
75
|
+
DEFINE FIELD IF NOT EXISTS leadAgentId ON TABLE planRun TYPE string;
|
|
76
|
+
DEFINE FIELD IF NOT EXISTS status ON TABLE planRun TYPE string;
|
|
77
|
+
DEFINE FIELD IF NOT EXISTS currentNodeId ON TABLE planRun TYPE option<string>;
|
|
78
|
+
DEFINE FIELD IF NOT EXISTS waitingNodeId ON TABLE planRun TYPE option<string>;
|
|
79
|
+
DEFINE FIELD IF NOT EXISTS readyNodeIds ON TABLE planRun TYPE array<string> DEFAULT [];
|
|
80
|
+
DEFINE FIELD IF NOT EXISTS failureCount ON TABLE planRun TYPE int DEFAULT 0;
|
|
81
|
+
DEFINE FIELD IF NOT EXISTS replacedRunId ON TABLE planRun TYPE option<record<planRun>>;
|
|
82
|
+
DEFINE FIELD IF NOT EXISTS lastCheckpointId ON TABLE planRun TYPE option<record<planCheckpoint>>;
|
|
83
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planRun TYPE datetime DEFAULT time::now() READONLY;
|
|
84
|
+
DEFINE FIELD IF NOT EXISTS updatedAt ON TABLE planRun TYPE datetime VALUE time::now();
|
|
85
|
+
DEFINE FIELD IF NOT EXISTS startedAt ON TABLE planRun TYPE option<datetime>;
|
|
86
|
+
DEFINE FIELD IF NOT EXISTS completedAt ON TABLE planRun TYPE option<datetime>;
|
|
87
|
+
|
|
88
|
+
DEFINE INDEX IF NOT EXISTS planRunWorkstreamIdx ON TABLE planRun COLUMNS workstreamId;
|
|
89
|
+
DEFINE INDEX IF NOT EXISTS planRunWorkstreamStatusIdx ON TABLE planRun COLUMNS workstreamId, status;
|
|
90
|
+
DEFINE INDEX IF NOT EXISTS planRunSpecIdx ON TABLE planRun COLUMNS planSpecId;
|
|
91
|
+
|
|
92
|
+
DEFINE TABLE IF NOT EXISTS planNodeRun SCHEMAFULL;
|
|
93
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planNodeRun TYPE record<planRun> REFERENCE ON DELETE CASCADE;
|
|
94
|
+
DEFINE FIELD IF NOT EXISTS planSpecId ON TABLE planNodeRun TYPE record<planSpec> REFERENCE ON DELETE CASCADE;
|
|
95
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planNodeRun TYPE string;
|
|
96
|
+
DEFINE FIELD IF NOT EXISTS status ON TABLE planNodeRun TYPE string;
|
|
97
|
+
DEFINE FIELD IF NOT EXISTS attemptCount ON TABLE planNodeRun TYPE int DEFAULT 0;
|
|
98
|
+
DEFINE FIELD IF NOT EXISTS retryCount ON TABLE planNodeRun TYPE int DEFAULT 0;
|
|
99
|
+
DEFINE FIELD IF NOT EXISTS resolvedInput ON TABLE planNodeRun TYPE option<object> FLEXIBLE;
|
|
100
|
+
DEFINE FIELD IF NOT EXISTS latestStructuredOutput ON TABLE planNodeRun TYPE option<object> FLEXIBLE;
|
|
101
|
+
DEFINE FIELD IF NOT EXISTS latestNotes ON TABLE planNodeRun TYPE option<string>;
|
|
102
|
+
DEFINE FIELD IF NOT EXISTS latestAttemptId ON TABLE planNodeRun TYPE option<record<planNodeAttempt>>;
|
|
103
|
+
DEFINE FIELD IF NOT EXISTS blockedReason ON TABLE planNodeRun TYPE option<string>;
|
|
104
|
+
DEFINE FIELD IF NOT EXISTS failureClass ON TABLE planNodeRun TYPE option<string>;
|
|
105
|
+
DEFINE FIELD IF NOT EXISTS readyAt ON TABLE planNodeRun TYPE option<datetime>;
|
|
106
|
+
DEFINE FIELD IF NOT EXISTS startedAt ON TABLE planNodeRun TYPE option<datetime>;
|
|
107
|
+
DEFINE FIELD IF NOT EXISTS completedAt ON TABLE planNodeRun TYPE option<datetime>;
|
|
108
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planNodeRun TYPE datetime DEFAULT time::now() READONLY;
|
|
109
|
+
DEFINE FIELD IF NOT EXISTS updatedAt ON TABLE planNodeRun TYPE datetime VALUE time::now();
|
|
110
|
+
|
|
111
|
+
DEFINE INDEX IF NOT EXISTS planNodeRunRunIdx ON TABLE planNodeRun COLUMNS runId;
|
|
112
|
+
DEFINE INDEX IF NOT EXISTS planNodeRunRunNodeIdx ON TABLE planNodeRun COLUMNS runId, nodeId UNIQUE;
|
|
113
|
+
DEFINE INDEX IF NOT EXISTS planNodeRunRunStatusIdx ON TABLE planNodeRun COLUMNS runId, status;
|
|
114
|
+
|
|
115
|
+
DEFINE TABLE IF NOT EXISTS planNodeAttempt SCHEMAFULL;
|
|
116
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planNodeAttempt TYPE record<planRun> REFERENCE ON DELETE CASCADE;
|
|
117
|
+
DEFINE FIELD IF NOT EXISTS nodeRunId ON TABLE planNodeAttempt TYPE record<planNodeRun> REFERENCE ON DELETE CASCADE;
|
|
118
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planNodeAttempt TYPE string;
|
|
119
|
+
DEFINE FIELD IF NOT EXISTS emittedBy ON TABLE planNodeAttempt TYPE string;
|
|
120
|
+
DEFINE FIELD IF NOT EXISTS status ON TABLE planNodeAttempt TYPE string;
|
|
121
|
+
DEFINE FIELD IF NOT EXISTS structuredOutput ON TABLE planNodeAttempt TYPE option<object> FLEXIBLE;
|
|
122
|
+
DEFINE FIELD IF NOT EXISTS notes ON TABLE planNodeAttempt TYPE option<string>;
|
|
123
|
+
DEFINE FIELD IF NOT EXISTS validationIssueIds ON TABLE planNodeAttempt TYPE array<record<planValidationIssue>> DEFAULT [];
|
|
124
|
+
DEFINE FIELD IF NOT EXISTS failureClass ON TABLE planNodeAttempt TYPE option<string>;
|
|
125
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planNodeAttempt TYPE datetime DEFAULT time::now() READONLY;
|
|
126
|
+
|
|
127
|
+
DEFINE INDEX IF NOT EXISTS planNodeAttemptRunIdx ON TABLE planNodeAttempt COLUMNS runId;
|
|
128
|
+
DEFINE INDEX IF NOT EXISTS planNodeAttemptNodeRunIdx ON TABLE planNodeAttempt COLUMNS nodeRunId;
|
|
129
|
+
DEFINE INDEX IF NOT EXISTS planNodeAttemptRunNodeIdx ON TABLE planNodeAttempt COLUMNS runId, nodeId;
|
|
130
|
+
|
|
131
|
+
DEFINE TABLE IF NOT EXISTS planArtifact SCHEMAFULL;
|
|
132
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planArtifact TYPE record<planRun> REFERENCE ON DELETE CASCADE;
|
|
133
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planArtifact TYPE string;
|
|
134
|
+
DEFINE FIELD IF NOT EXISTS attemptId ON TABLE planArtifact TYPE record<planNodeAttempt> REFERENCE ON DELETE CASCADE;
|
|
135
|
+
DEFINE FIELD IF NOT EXISTS name ON TABLE planArtifact TYPE string;
|
|
136
|
+
DEFINE FIELD IF NOT EXISTS kind ON TABLE planArtifact TYPE string;
|
|
137
|
+
DEFINE FIELD IF NOT EXISTS pointer ON TABLE planArtifact TYPE string;
|
|
138
|
+
DEFINE FIELD IF NOT EXISTS schemaRef ON TABLE planArtifact TYPE option<string>;
|
|
139
|
+
DEFINE FIELD IF NOT EXISTS description ON TABLE planArtifact TYPE option<string>;
|
|
140
|
+
DEFINE FIELD IF NOT EXISTS payload ON TABLE planArtifact TYPE option<object | array> FLEXIBLE;
|
|
141
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planArtifact TYPE datetime DEFAULT time::now() READONLY;
|
|
142
|
+
|
|
143
|
+
DEFINE INDEX IF NOT EXISTS planArtifactRunIdx ON TABLE planArtifact COLUMNS runId;
|
|
144
|
+
DEFINE INDEX IF NOT EXISTS planArtifactAttemptIdx ON TABLE planArtifact COLUMNS attemptId;
|
|
145
|
+
DEFINE INDEX IF NOT EXISTS planArtifactRunNodeNameIdx ON TABLE planArtifact COLUMNS runId, nodeId, name;
|
|
146
|
+
|
|
147
|
+
DEFINE TABLE IF NOT EXISTS planValidationIssue SCHEMAFULL;
|
|
148
|
+
DEFINE FIELD IF NOT EXISTS planSpecId ON TABLE planValidationIssue TYPE option<record<planSpec>>;
|
|
149
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planValidationIssue TYPE option<record<planRun>>;
|
|
150
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planValidationIssue TYPE option<string>;
|
|
151
|
+
DEFINE FIELD IF NOT EXISTS attemptId ON TABLE planValidationIssue TYPE option<record<planNodeAttempt>>;
|
|
152
|
+
DEFINE FIELD IF NOT EXISTS severity ON TABLE planValidationIssue TYPE string;
|
|
153
|
+
DEFINE FIELD IF NOT EXISTS code ON TABLE planValidationIssue TYPE string;
|
|
154
|
+
DEFINE FIELD IF NOT EXISTS message ON TABLE planValidationIssue TYPE string;
|
|
155
|
+
DEFINE FIELD IF NOT EXISTS detail ON TABLE planValidationIssue TYPE option<object> FLEXIBLE;
|
|
156
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planValidationIssue TYPE datetime DEFAULT time::now() READONLY;
|
|
157
|
+
|
|
158
|
+
DEFINE INDEX IF NOT EXISTS planValidationIssueSpecIdx ON TABLE planValidationIssue COLUMNS planSpecId;
|
|
159
|
+
DEFINE INDEX IF NOT EXISTS planValidationIssueRunIdx ON TABLE planValidationIssue COLUMNS runId;
|
|
160
|
+
DEFINE INDEX IF NOT EXISTS planValidationIssueAttemptIdx ON TABLE planValidationIssue COLUMNS attemptId;
|
|
161
|
+
|
|
162
|
+
DEFINE TABLE IF NOT EXISTS planCheckpoint SCHEMAFULL;
|
|
163
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planCheckpoint TYPE record<planRun> REFERENCE ON DELETE CASCADE;
|
|
164
|
+
DEFINE FIELD IF NOT EXISTS sequence ON TABLE planCheckpoint TYPE int;
|
|
165
|
+
DEFINE FIELD IF NOT EXISTS runStatus ON TABLE planCheckpoint TYPE string;
|
|
166
|
+
DEFINE FIELD IF NOT EXISTS readyNodeIds ON TABLE planCheckpoint TYPE array<string> DEFAULT [];
|
|
167
|
+
DEFINE FIELD IF NOT EXISTS activeNodeIds ON TABLE planCheckpoint TYPE array<string> DEFAULT [];
|
|
168
|
+
DEFINE FIELD IF NOT EXISTS artifactIds ON TABLE planCheckpoint TYPE array<record<planArtifact>> DEFAULT [];
|
|
169
|
+
DEFINE FIELD IF NOT EXISTS lastCompletedNodeIds ON TABLE planCheckpoint TYPE array<string> DEFAULT [];
|
|
170
|
+
DEFINE FIELD IF NOT EXISTS snapshot ON TABLE planCheckpoint TYPE object FLEXIBLE;
|
|
171
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planCheckpoint TYPE datetime DEFAULT time::now() READONLY;
|
|
172
|
+
|
|
173
|
+
DEFINE INDEX IF NOT EXISTS planCheckpointRunIdx ON TABLE planCheckpoint COLUMNS runId;
|
|
174
|
+
DEFINE INDEX IF NOT EXISTS planCheckpointRunSequenceIdx ON TABLE planCheckpoint COLUMNS runId, sequence UNIQUE;
|
|
175
|
+
|
|
176
|
+
DEFINE TABLE IF NOT EXISTS planApproval SCHEMAFULL;
|
|
177
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planApproval TYPE record<planRun> REFERENCE ON DELETE CASCADE;
|
|
178
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planApproval TYPE string;
|
|
179
|
+
DEFINE FIELD IF NOT EXISTS nodeRunId ON TABLE planApproval TYPE record<planNodeRun> REFERENCE ON DELETE CASCADE;
|
|
180
|
+
DEFINE FIELD IF NOT EXISTS status ON TABLE planApproval TYPE string;
|
|
181
|
+
DEFINE FIELD IF NOT EXISTS presented ON TABLE planApproval TYPE option<object> FLEXIBLE;
|
|
182
|
+
DEFINE FIELD IF NOT EXISTS response ON TABLE planApproval TYPE option<object> FLEXIBLE;
|
|
183
|
+
DEFINE FIELD IF NOT EXISTS requestedBy ON TABLE planApproval TYPE string;
|
|
184
|
+
DEFINE FIELD IF NOT EXISTS respondedBy ON TABLE planApproval TYPE option<string>;
|
|
185
|
+
DEFINE FIELD IF NOT EXISTS approvalMessageId ON TABLE planApproval TYPE option<string>;
|
|
186
|
+
DEFINE FIELD IF NOT EXISTS comments ON TABLE planApproval TYPE option<string>;
|
|
187
|
+
DEFINE FIELD IF NOT EXISTS requiredEdits ON TABLE planApproval TYPE array<string> DEFAULT [];
|
|
188
|
+
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planApproval TYPE datetime DEFAULT time::now() READONLY;
|
|
189
|
+
DEFINE FIELD IF NOT EXISTS updatedAt ON TABLE planApproval TYPE datetime VALUE time::now();
|
|
190
|
+
DEFINE FIELD IF NOT EXISTS respondedAt ON TABLE planApproval TYPE option<datetime>;
|
|
191
|
+
|
|
192
|
+
DEFINE INDEX IF NOT EXISTS planApprovalRunIdx ON TABLE planApproval COLUMNS runId;
|
|
193
|
+
DEFINE INDEX IF NOT EXISTS planApprovalNodeRunIdx ON TABLE planApproval COLUMNS nodeRunId;
|
|
194
|
+
DEFINE INDEX IF NOT EXISTS planApprovalRunStatusIdx ON TABLE planApproval COLUMNS runId, status;
|
|
47
195
|
|
|
48
196
|
DEFINE TABLE IF NOT EXISTS planEvent SCHEMAFULL;
|
|
49
|
-
DEFINE FIELD IF NOT EXISTS
|
|
50
|
-
DEFINE FIELD IF NOT EXISTS
|
|
197
|
+
DEFINE FIELD IF NOT EXISTS planSpecId ON TABLE planEvent TYPE record<planSpec> REFERENCE ON DELETE CASCADE;
|
|
198
|
+
DEFINE FIELD IF NOT EXISTS runId ON TABLE planEvent TYPE record<planRun> REFERENCE ON DELETE CASCADE;
|
|
199
|
+
DEFINE FIELD IF NOT EXISTS nodeId ON TABLE planEvent TYPE option<string>;
|
|
200
|
+
DEFINE FIELD IF NOT EXISTS attemptId ON TABLE planEvent TYPE option<record<planNodeAttempt>>;
|
|
201
|
+
DEFINE FIELD IF NOT EXISTS approvalId ON TABLE planEvent TYPE option<record<planApproval>>;
|
|
51
202
|
DEFINE FIELD IF NOT EXISTS eventType ON TABLE planEvent TYPE string;
|
|
52
203
|
DEFINE FIELD IF NOT EXISTS fromStatus ON TABLE planEvent TYPE option<string>;
|
|
53
204
|
DEFINE FIELD IF NOT EXISTS toStatus ON TABLE planEvent TYPE option<string>;
|
|
@@ -56,7 +207,6 @@ DEFINE FIELD IF NOT EXISTS detail ON TABLE planEvent TYPE option<object> FLEXIBL
|
|
|
56
207
|
DEFINE FIELD IF NOT EXISTS emittedBy ON TABLE planEvent TYPE string;
|
|
57
208
|
DEFINE FIELD IF NOT EXISTS createdAt ON TABLE planEvent TYPE datetime DEFAULT time::now() READONLY;
|
|
58
209
|
|
|
59
|
-
DEFINE INDEX IF NOT EXISTS
|
|
60
|
-
DEFINE INDEX IF NOT EXISTS
|
|
61
|
-
DEFINE INDEX IF NOT EXISTS
|
|
62
|
-
DEFINE INDEX IF NOT EXISTS planEventTimeIdx ON TABLE planEvent COLUMNS planId, createdAt;
|
|
210
|
+
DEFINE INDEX IF NOT EXISTS planEventRunIdx ON TABLE planEvent COLUMNS runId;
|
|
211
|
+
DEFINE INDEX IF NOT EXISTS planEventRunNodeIdx ON TABLE planEvent COLUMNS runId, nodeId;
|
|
212
|
+
DEFINE INDEX IF NOT EXISTS planEventRunTimeIdx ON TABLE planEvent COLUMNS runId, createdAt;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -112,9 +112,10 @@
|
|
|
112
112
|
"registry": "https://registry.npmjs.org/"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
+
"@ai-sdk/devtools": "^0.0.15",
|
|
115
116
|
"@ai-sdk/openai": "^3.0.41",
|
|
116
117
|
"@logtape/logtape": "^2.0.4",
|
|
117
|
-
"@lota-sdk/shared": "0.1.
|
|
118
|
+
"@lota-sdk/shared": "0.1.9",
|
|
118
119
|
"@mendable/firecrawl-js": "^4.16.0",
|
|
119
120
|
"@surrealdb/node": "^3.0.3",
|
|
120
121
|
"ai": "^6.0.116",
|
|
@@ -123,6 +124,7 @@
|
|
|
123
124
|
"ioredis": "5.9.3",
|
|
124
125
|
"mammoth": "^1.12.0",
|
|
125
126
|
"pdf-parse": "^2.4.5",
|
|
127
|
+
"resumable-stream": "^2.2.12",
|
|
126
128
|
"surrealdb": "^2.0.2",
|
|
127
129
|
"zod": "^4.3.6"
|
|
128
130
|
}
|
package/src/bifrost/bifrost.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devToolsMiddleware } from '@ai-sdk/devtools'
|
|
1
2
|
import { createOpenAI } from '@ai-sdk/openai'
|
|
2
3
|
import { wrapLanguageModel } from 'ai'
|
|
3
4
|
import type { LanguageModelMiddleware } from 'ai'
|
|
@@ -36,6 +37,10 @@ function readReasoningDetailsText(value: unknown): string | null {
|
|
|
36
37
|
return textParts.join('\n\n')
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
function readReasoningDeltaText(value: unknown): string | null {
|
|
41
|
+
return typeof value === 'string' && value.length > 0 ? value : null
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
function readBifrostChatReasoningText(message: Record<string, unknown>): string | null {
|
|
40
45
|
return (
|
|
41
46
|
readString(message.reasoning) ??
|
|
@@ -63,13 +68,29 @@ export function extractBifrostChatReasoningDeltaText(rawChunk: unknown): string
|
|
|
63
68
|
for (const choice of rawChunk.choices) {
|
|
64
69
|
if (!isRecord(choice) || !isRecord(choice.delta)) continue
|
|
65
70
|
|
|
66
|
-
const reasoningText =
|
|
71
|
+
const reasoningText =
|
|
72
|
+
readReasoningDeltaText(choice.delta.reasoning) ??
|
|
73
|
+
readReasoningDeltaText(choice.delta.reasoning_content) ??
|
|
74
|
+
readReasoningDetailsText(choice.delta.reasoning_details)
|
|
67
75
|
if (reasoningText) return reasoningText
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
return null
|
|
71
79
|
}
|
|
72
80
|
|
|
81
|
+
type BifrostResponsesReasoningDelta = { id: string; delta: string; itemId: string }
|
|
82
|
+
|
|
83
|
+
export function extractBifrostResponsesReasoningDelta(rawChunk: unknown): BifrostResponsesReasoningDelta | null {
|
|
84
|
+
if (!isRecord(rawChunk) || rawChunk.type !== 'response.reasoning_summary_text.delta') return null
|
|
85
|
+
if ('summary_index' in rawChunk) return null
|
|
86
|
+
|
|
87
|
+
const itemId = readString(rawChunk.item_id)
|
|
88
|
+
const delta = readReasoningDeltaText(rawChunk.delta)
|
|
89
|
+
if (!itemId || !delta) return null
|
|
90
|
+
|
|
91
|
+
return { id: `${itemId}:0`, delta, itemId }
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
export function injectBifrostChatReasoningContent(
|
|
74
95
|
content: readonly BifrostGeneratedContent[],
|
|
75
96
|
response?: BifrostChatResponse,
|
|
@@ -143,6 +164,30 @@ export function injectBifrostChatReasoningStream(
|
|
|
143
164
|
)
|
|
144
165
|
}
|
|
145
166
|
|
|
167
|
+
export function injectBifrostResponsesReasoningStream(
|
|
168
|
+
stream: ReadableStream<BifrostStreamPart>,
|
|
169
|
+
): ReadableStream<BifrostStreamPart> {
|
|
170
|
+
return stream.pipeThrough(
|
|
171
|
+
new TransformStream<BifrostStreamPart, BifrostStreamPart>({
|
|
172
|
+
transform(chunk, controller) {
|
|
173
|
+
controller.enqueue(chunk)
|
|
174
|
+
|
|
175
|
+
if (chunk.type !== 'raw') return
|
|
176
|
+
|
|
177
|
+
const reasoningDelta = extractBifrostResponsesReasoningDelta(chunk.rawValue)
|
|
178
|
+
if (!reasoningDelta) return
|
|
179
|
+
|
|
180
|
+
controller.enqueue({
|
|
181
|
+
type: 'reasoning-delta',
|
|
182
|
+
id: reasoningDelta.id,
|
|
183
|
+
delta: reasoningDelta.delta,
|
|
184
|
+
providerMetadata: { openai: { itemId: reasoningDelta.itemId } },
|
|
185
|
+
} as BifrostStreamPart)
|
|
186
|
+
},
|
|
187
|
+
}),
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
146
191
|
function addBifrostReasoningRawChunks(
|
|
147
192
|
params: BifrostCallOptions,
|
|
148
193
|
type: BifrostTransformParamsOptions['type'],
|
|
@@ -197,6 +242,14 @@ function createBifrostProvider(extraParams?: BifrostExtraParams) {
|
|
|
197
242
|
})
|
|
198
243
|
}
|
|
199
244
|
|
|
245
|
+
function withBifrostDevTools<TModel>(model: TModel): TModel {
|
|
246
|
+
if (process.env.NODE_ENV === 'production') {
|
|
247
|
+
return model
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return wrapLanguageModel({ model: model as never, middleware: devToolsMiddleware() }) as unknown as TModel
|
|
251
|
+
}
|
|
252
|
+
|
|
200
253
|
let provider: ReturnType<typeof createOpenAI> | null = null
|
|
201
254
|
let openRouterResponseHealingProvider: ReturnType<typeof createOpenAI> | null = null
|
|
202
255
|
|
|
@@ -217,38 +270,54 @@ export function getBifrostOpenRouterResponseHealingProvider() {
|
|
|
217
270
|
}
|
|
218
271
|
|
|
219
272
|
export function bifrostModel(modelId: string) {
|
|
220
|
-
return
|
|
273
|
+
return withBifrostDevTools(
|
|
274
|
+
wrapLanguageModel({
|
|
275
|
+
model: getBifrostProvider()(modelId),
|
|
276
|
+
middleware: {
|
|
277
|
+
specificationVersion: 'v3',
|
|
278
|
+
transformParams: async ({ params, type }) => addBifrostReasoningRawChunks(params, type),
|
|
279
|
+
wrapStream: async ({ doStream, params }) => {
|
|
280
|
+
const result = await doStream()
|
|
281
|
+
if (!isReasoningEnabled(params)) return result
|
|
282
|
+
|
|
283
|
+
return { ...result, stream: injectBifrostResponsesReasoningStream(result.stream) }
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
}),
|
|
287
|
+
)
|
|
221
288
|
}
|
|
222
289
|
|
|
223
290
|
export function bifrostOpenRouterResponseHealingModel(modelId: string) {
|
|
224
|
-
return getBifrostOpenRouterResponseHealingProvider()(modelId)
|
|
291
|
+
return withBifrostDevTools(getBifrostOpenRouterResponseHealingProvider()(modelId))
|
|
225
292
|
}
|
|
226
293
|
|
|
227
294
|
export function bifrostChatModel(modelId: string) {
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
295
|
+
return withBifrostDevTools(
|
|
296
|
+
wrapLanguageModel({
|
|
297
|
+
model: getBifrostProvider().chat(modelId),
|
|
298
|
+
middleware: {
|
|
299
|
+
specificationVersion: 'v3',
|
|
300
|
+
transformParams: async ({ params, type }) => addBifrostReasoningRawChunks(params, type),
|
|
301
|
+
wrapGenerate: async ({ doGenerate }) => {
|
|
302
|
+
const result = await doGenerate()
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
...result,
|
|
306
|
+
content: injectBifrostChatReasoningContent(
|
|
307
|
+
result.content,
|
|
308
|
+
result.response as BifrostChatResponse | undefined,
|
|
309
|
+
),
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
wrapStream: async ({ doStream, params }) => {
|
|
313
|
+
const result = await doStream()
|
|
314
|
+
if (!isReasoningEnabled(params)) return result
|
|
247
315
|
|
|
248
|
-
|
|
316
|
+
return { ...result, stream: injectBifrostChatReasoningStream(result.stream) }
|
|
317
|
+
},
|
|
249
318
|
},
|
|
250
|
-
},
|
|
251
|
-
|
|
319
|
+
}),
|
|
320
|
+
)
|
|
252
321
|
}
|
|
253
322
|
|
|
254
323
|
export function bifrostEmbeddingModel(modelId: string) {
|
|
@@ -10,26 +10,28 @@ export const OPENROUTER_CODE_ANALYSIS_MODEL_ID = 'openrouter/xiaomi/mimo-v2-flas
|
|
|
10
10
|
export const OPENROUTER_FAST_REASONING_MODEL_ID = 'openrouter/openai/gpt-oss-120b:nitro' as const
|
|
11
11
|
export const OPENROUTER_STRUCTURED_REASONING_MODEL_ID = 'openrouter/openai/gpt-oss-120b:exacto' as const
|
|
12
12
|
|
|
13
|
+
export const BIFROST_REASONING_SUMMARY_LEVEL = 'detailed' as const
|
|
14
|
+
|
|
13
15
|
export const OPENAI_HIGH_REASONING_PROVIDER_OPTIONS = {
|
|
14
|
-
openai: { forceReasoning: true, reasoningEffort: 'high', reasoningSummary:
|
|
16
|
+
openai: { forceReasoning: true, reasoningEffort: 'high', reasoningSummary: BIFROST_REASONING_SUMMARY_LEVEL },
|
|
15
17
|
} as const
|
|
16
18
|
|
|
17
19
|
export const OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS = {
|
|
18
|
-
openai: { forceReasoning: true, reasoningEffort: 'high', reasoningSummary:
|
|
20
|
+
openai: { forceReasoning: true, reasoningEffort: 'high', reasoningSummary: BIFROST_REASONING_SUMMARY_LEVEL },
|
|
19
21
|
} as const
|
|
20
22
|
|
|
21
23
|
export const OPENROUTER_XHIGH_REASONING_PROVIDER_OPTIONS = {
|
|
22
|
-
openai: { forceReasoning: true, reasoningEffort: 'xhigh', reasoningSummary:
|
|
24
|
+
openai: { forceReasoning: true, reasoningEffort: 'xhigh', reasoningSummary: BIFROST_REASONING_SUMMARY_LEVEL },
|
|
23
25
|
} as const
|
|
24
26
|
|
|
25
27
|
export const OPENROUTER_MEDIUM_REASONING_PROVIDER_OPTIONS = {
|
|
26
|
-
openai: { forceReasoning: true, reasoningEffort: 'medium', reasoningSummary:
|
|
28
|
+
openai: { forceReasoning: true, reasoningEffort: 'medium', reasoningSummary: BIFROST_REASONING_SUMMARY_LEVEL },
|
|
27
29
|
} as const
|
|
28
30
|
|
|
29
31
|
export const OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS = {
|
|
30
|
-
openai: { forceReasoning: true, reasoningEffort: 'low', reasoningSummary:
|
|
32
|
+
openai: { forceReasoning: true, reasoningEffort: 'low', reasoningSummary: BIFROST_REASONING_SUMMARY_LEVEL },
|
|
31
33
|
} as const
|
|
32
34
|
|
|
33
35
|
export const OPENROUTER_MINIMAL_REASONING_PROVIDER_OPTIONS = {
|
|
34
|
-
openai: { forceReasoning: true, reasoningEffort: 'minimal', reasoningSummary:
|
|
36
|
+
openai: { forceReasoning: true, reasoningEffort: 'minimal', reasoningSummary: BIFROST_REASONING_SUMMARY_LEVEL },
|
|
35
37
|
} as const
|
package/src/db/memory-store.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { BoundQuery, eq, inside } from 'surrealdb'
|
|
2
2
|
|
|
3
|
-
import { getEmbeddingCache } from '../ai/embedding-cache'
|
|
4
|
-
import { env } from '../config/env-shapes'
|
|
5
3
|
import { aiLogger } from '../config/logger'
|
|
6
4
|
import { DEFAULT_MEMORY_SEARCH_LIMIT } from '../config/search'
|
|
7
|
-
import {
|
|
5
|
+
import { getDefaultEmbeddings } from '../embeddings/provider'
|
|
8
6
|
import { memoryQueryBuilder } from './memory-query-builder'
|
|
9
7
|
import type { RelationCounts } from './memory-store.helpers'
|
|
10
8
|
import { hashContent, mapRowToMemoryRecord, processGraphAwareRows } from './memory-store.helpers'
|
|
@@ -25,8 +23,6 @@ import { TABLES } from './tables'
|
|
|
25
23
|
const MEMORY_TABLE = TABLES.MEMORY
|
|
26
24
|
const MEMORY_HISTORY_TABLE = TABLES.MEMORY_HISTORY
|
|
27
25
|
const MEMORY_RELATION_TABLE = TABLES.MEMORY_RELATION
|
|
28
|
-
const EMBEDDING_CACHE_TTL_MS = 5 * 60 * 1000
|
|
29
|
-
const EMBEDDING_CACHE_MAX_ENTRIES = 64
|
|
30
26
|
const MIN_RELEVANCE_SCORE = 0.25
|
|
31
27
|
const TOUCH_MEMORIES_MAX_ATTEMPTS = 4
|
|
32
28
|
const TOUCH_MEMORIES_RETRY_BASE_DELAY_MS = 25
|
|
@@ -37,9 +33,6 @@ interface EmbeddingClient {
|
|
|
37
33
|
}
|
|
38
34
|
|
|
39
35
|
export class SurrealMemoryStore {
|
|
40
|
-
private embeddingCache = new Map<string, { embedding: number[]; ts: number }>()
|
|
41
|
-
private embeddingInFlight = new Map<string, Promise<number[]>>()
|
|
42
|
-
|
|
43
36
|
constructor(private embeddings: EmbeddingClient) {}
|
|
44
37
|
|
|
45
38
|
private toMetadataFieldPath(key: string): string {
|
|
@@ -183,72 +176,11 @@ export class SurrealMemoryStore {
|
|
|
183
176
|
}))
|
|
184
177
|
}
|
|
185
178
|
|
|
186
|
-
private getEmbeddingCacheKey(content: string): string {
|
|
187
|
-
return content.trim().toLowerCase()
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private pruneEmbeddingCache(now: number): void {
|
|
191
|
-
for (const [cacheKey, entry] of this.embeddingCache.entries()) {
|
|
192
|
-
if (now - entry.ts > EMBEDDING_CACHE_TTL_MS) {
|
|
193
|
-
this.embeddingCache.delete(cacheKey)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
while (this.embeddingCache.size > EMBEDDING_CACHE_MAX_ENTRIES) {
|
|
198
|
-
const oldest = this.embeddingCache.keys().next().value
|
|
199
|
-
if (!oldest) break
|
|
200
|
-
this.embeddingCache.delete(oldest)
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
179
|
private async generateEmbedding(content: string): Promise<number[]> {
|
|
205
180
|
const normalized = content.trim()
|
|
206
181
|
if (!normalized) return []
|
|
207
182
|
|
|
208
|
-
|
|
209
|
-
const now = Date.now()
|
|
210
|
-
this.pruneEmbeddingCache(now)
|
|
211
|
-
|
|
212
|
-
// L1: in-memory cache
|
|
213
|
-
const cached = this.embeddingCache.get(cacheKey)
|
|
214
|
-
if (cached && now - cached.ts <= EMBEDDING_CACHE_TTL_MS) {
|
|
215
|
-
return cached.embedding
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const inFlight = this.embeddingInFlight.get(cacheKey)
|
|
219
|
-
if (inFlight) {
|
|
220
|
-
return await inFlight
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const request = (async () => {
|
|
224
|
-
// L2: Redis cache
|
|
225
|
-
const redisCache = getEmbeddingCache()
|
|
226
|
-
if (redisCache) {
|
|
227
|
-
const model = env.AI_EMBEDDING_MODEL
|
|
228
|
-
const redisCached = await redisCache.get(model, normalized)
|
|
229
|
-
if (redisCached) {
|
|
230
|
-
this.embeddingCache.set(cacheKey, { embedding: redisCached, ts: Date.now() })
|
|
231
|
-
return redisCached
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// L3: API call
|
|
236
|
-
const embedding = await this.embeddings.embedQuery(normalized)
|
|
237
|
-
this.embeddingCache.set(cacheKey, { embedding, ts: Date.now() })
|
|
238
|
-
this.pruneEmbeddingCache(Date.now())
|
|
239
|
-
|
|
240
|
-
// Backfill Redis
|
|
241
|
-
if (redisCache) {
|
|
242
|
-
void redisCache.set(env.AI_EMBEDDING_MODEL, normalized, embedding)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return embedding
|
|
246
|
-
})().finally(() => {
|
|
247
|
-
this.embeddingInFlight.delete(cacheKey)
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
this.embeddingInFlight.set(cacheKey, request)
|
|
251
|
-
return await request
|
|
183
|
+
return await this.embeddings.embedQuery(normalized)
|
|
252
184
|
}
|
|
253
185
|
|
|
254
186
|
async warmEmbedding(content: string): Promise<void> {
|
|
@@ -1059,7 +991,7 @@ let defaultMemoryStore: SurrealMemoryStore | null = null
|
|
|
1059
991
|
|
|
1060
992
|
export function getDefaultMemoryStore(): SurrealMemoryStore {
|
|
1061
993
|
if (!defaultMemoryStore) {
|
|
1062
|
-
defaultMemoryStore = new SurrealMemoryStore(
|
|
994
|
+
defaultMemoryStore = new SurrealMemoryStore(getDefaultEmbeddings())
|
|
1063
995
|
}
|
|
1064
996
|
return defaultMemoryStore
|
|
1065
997
|
}
|