@ekairos/structure 1.22.4-beta.development.0 → 1.22.4-beta.feature-thread-unify.0
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/dataset/steps.js +42 -27
- package/dist/file/steps.js +2 -2
- package/dist/rowsOutputPaging.js +9 -5
- package/dist/rowsOutputSplit.js +3 -4
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +4 -4
- package/dist/sandbox/steps.js +10 -10
- package/dist/schema.js +4 -4
- package/dist/service.d.ts +1 -1
- package/dist/service.js +34 -15
- package/dist/steps/commitFromEvents.step.js +5 -5
- package/dist/steps/persistObjectFromStory.step.js +2 -2
- package/dist/structure.js +101 -163
- package/package.json +5 -5
- package/dist/contextPersistence.d.ts +0 -29
- package/dist/contextPersistence.js +0 -62
- package/dist/steps/ensureExecutionTrail.step.d.ts +0 -11
- package/dist/steps/ensureExecutionTrail.step.js +0 -100
package/dist/dataset/steps.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { findStructureContextByKey, linkStructureOutputFileToContextByKey, unlinkStructureOutputFileFromContextByKey, } from "../contextPersistence.js";
|
|
2
1
|
export async function structureGetOrCreateContextStep(params) {
|
|
3
2
|
"use step";
|
|
4
3
|
try {
|
|
5
|
-
const {
|
|
6
|
-
const runtime = await
|
|
4
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
5
|
+
const runtime = await getThreadRuntime(params.env);
|
|
7
6
|
const ctx = await runtime.store.getOrCreateContext({ key: params.contextKey });
|
|
8
7
|
return { ok: true, data: ctx };
|
|
9
8
|
}
|
|
@@ -15,8 +14,8 @@ export async function structureGetOrCreateContextStep(params) {
|
|
|
15
14
|
export async function structureGetContextStep(params) {
|
|
16
15
|
"use step";
|
|
17
16
|
try {
|
|
18
|
-
const {
|
|
19
|
-
const runtime = await
|
|
17
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
18
|
+
const runtime = await getThreadRuntime(params.env);
|
|
20
19
|
const ctx = await runtime.store.getContext({ key: params.contextKey });
|
|
21
20
|
if (!ctx)
|
|
22
21
|
return { ok: false, error: "Context not found" };
|
|
@@ -30,8 +29,8 @@ export async function structureGetContextStep(params) {
|
|
|
30
29
|
export async function structureUpdateContextContentStep(params) {
|
|
31
30
|
"use step";
|
|
32
31
|
try {
|
|
33
|
-
const {
|
|
34
|
-
const runtime = await
|
|
32
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
33
|
+
const runtime = await getThreadRuntime(params.env);
|
|
35
34
|
const updated = await runtime.store.updateContextContent({ key: params.contextKey }, params.content);
|
|
36
35
|
return { ok: true, data: updated };
|
|
37
36
|
}
|
|
@@ -43,8 +42,8 @@ export async function structureUpdateContextContentStep(params) {
|
|
|
43
42
|
export async function structurePatchContextContentStep(params) {
|
|
44
43
|
"use step";
|
|
45
44
|
try {
|
|
46
|
-
const {
|
|
47
|
-
const runtime = await
|
|
45
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
46
|
+
const runtime = await getThreadRuntime(params.env);
|
|
48
47
|
const existing = await runtime.store.getOrCreateContext({ key: params.contextKey });
|
|
49
48
|
const existingContent = (existing?.content ?? {});
|
|
50
49
|
const existingStructure = (existingContent?.structure ?? {});
|
|
@@ -66,8 +65,8 @@ export async function structureUploadRowsOutputJsonlStep(params) {
|
|
|
66
65
|
"use step";
|
|
67
66
|
const startedAt = Date.now();
|
|
68
67
|
try {
|
|
69
|
-
const {
|
|
70
|
-
const runtime = await
|
|
68
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
69
|
+
const runtime = await getThreadRuntime(params.env);
|
|
71
70
|
const db = runtime.db;
|
|
72
71
|
const storagePath = `/structure/${params.structureId}/output.jsonl`;
|
|
73
72
|
const fileBuffer = Buffer.from(params.contentBase64 ?? "", "base64");
|
|
@@ -90,12 +89,15 @@ export async function structureLinkRowsOutputFileToContextStep(params) {
|
|
|
90
89
|
"use step";
|
|
91
90
|
const startedAt = Date.now();
|
|
92
91
|
try {
|
|
93
|
-
const {
|
|
94
|
-
const runtime = await
|
|
92
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
93
|
+
const runtime = await getThreadRuntime(params.env);
|
|
95
94
|
const store = runtime.store;
|
|
96
95
|
const db = runtime.db;
|
|
97
|
-
await store.getOrCreateContext({ key: params.contextKey });
|
|
98
|
-
|
|
96
|
+
const ctx = await store.getOrCreateContext({ key: params.contextKey });
|
|
97
|
+
const ctxId = ctx?.id;
|
|
98
|
+
if (!ctxId)
|
|
99
|
+
return { ok: false, error: "Context not found" };
|
|
100
|
+
await db.transact([db.tx.thread_contexts[ctxId].link({ structure_output_file: params.fileId })]);
|
|
99
101
|
console.log(`[structure:link-jsonl] contextKey=${params.contextKey} fileId=${params.fileId} elapsedMs=${Date.now() - startedAt}`);
|
|
100
102
|
return { ok: true };
|
|
101
103
|
}
|
|
@@ -107,12 +109,15 @@ export async function structureLinkRowsOutputFileToContextStep(params) {
|
|
|
107
109
|
export async function structureUnlinkRowsOutputFileFromContextStep(params) {
|
|
108
110
|
"use step";
|
|
109
111
|
try {
|
|
110
|
-
const {
|
|
111
|
-
const runtime = await
|
|
112
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
113
|
+
const runtime = await getThreadRuntime(params.env);
|
|
112
114
|
const store = runtime.store;
|
|
113
115
|
const db = runtime.db;
|
|
114
|
-
await store.getOrCreateContext({ key: params.contextKey });
|
|
115
|
-
|
|
116
|
+
const ctx = await store.getOrCreateContext({ key: params.contextKey });
|
|
117
|
+
const ctxId = ctx?.id;
|
|
118
|
+
if (!ctxId)
|
|
119
|
+
return { ok: false, error: "Context not found" };
|
|
120
|
+
await db.transact([db.tx.thread_contexts[ctxId].unlink({ structure_output_file: params.fileId })]);
|
|
116
121
|
return { ok: true };
|
|
117
122
|
}
|
|
118
123
|
catch (error) {
|
|
@@ -123,11 +128,16 @@ export async function structureUnlinkRowsOutputFileFromContextStep(params) {
|
|
|
123
128
|
export async function structureGetContextWithRowsOutputFileStep(params) {
|
|
124
129
|
"use step";
|
|
125
130
|
try {
|
|
126
|
-
const {
|
|
127
|
-
const runtime = await
|
|
131
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
132
|
+
const runtime = await getThreadRuntime(params.env);
|
|
128
133
|
const db = runtime.db;
|
|
129
|
-
const
|
|
130
|
-
|
|
134
|
+
const query = (await db.query({
|
|
135
|
+
thread_contexts: {
|
|
136
|
+
$: { where: { key: params.contextKey }, limit: 1 },
|
|
137
|
+
structure_output_file: {},
|
|
138
|
+
},
|
|
139
|
+
}));
|
|
140
|
+
const row = query.thread_contexts?.[0];
|
|
131
141
|
if (!row)
|
|
132
142
|
return { ok: false, error: "Context not found" };
|
|
133
143
|
return { ok: true, data: row };
|
|
@@ -142,11 +152,16 @@ export async function structureReadRowsOutputJsonlStep(params) {
|
|
|
142
152
|
const startedAt = Date.now();
|
|
143
153
|
try {
|
|
144
154
|
const contextKey = `structure:${params.structureId}`;
|
|
145
|
-
const {
|
|
146
|
-
const runtime = await
|
|
155
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
156
|
+
const runtime = await getThreadRuntime(params.env);
|
|
147
157
|
const db = runtime.db;
|
|
148
|
-
const
|
|
149
|
-
|
|
158
|
+
const query = (await db.query({
|
|
159
|
+
thread_contexts: {
|
|
160
|
+
$: { where: { key: contextKey }, limit: 1 },
|
|
161
|
+
structure_output_file: {},
|
|
162
|
+
},
|
|
163
|
+
}));
|
|
164
|
+
const ctx = query.thread_contexts?.[0];
|
|
150
165
|
if (!ctx)
|
|
151
166
|
return { ok: false, error: "Context not found" };
|
|
152
167
|
const linked = Array.isArray(ctx?.structure_output_file) ? ctx.structure_output_file[0] : ctx.structure_output_file;
|
package/dist/file/steps.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export async function readInstantFileStep(params) {
|
|
2
2
|
"use step";
|
|
3
|
-
const {
|
|
4
|
-
const runtime = (await
|
|
3
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
4
|
+
const runtime = (await getThreadRuntime(params.env));
|
|
5
5
|
const db = runtime.db;
|
|
6
6
|
const { DatasetService } = await import("../service.js");
|
|
7
7
|
const service = new DatasetService(db);
|
package/dist/rowsOutputPaging.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getDatasetOutputPath, getDatasetWorkstation } from "./datasetFiles.js";
|
|
2
2
|
import { createDatasetSandboxStep, runDatasetSandboxCommandStep } from "./sandbox/steps.js";
|
|
3
|
-
import {
|
|
4
|
-
import { getContextRuntime } from "./runtime.js";
|
|
3
|
+
import { getThreadRuntime } from "./runtime.js";
|
|
5
4
|
/**
|
|
6
5
|
* Step 1/2:
|
|
7
6
|
* Download the rows output.jsonl from Instant storage into a sandbox file.
|
|
@@ -38,11 +37,16 @@ export async function structureDownloadRowsOutputToSandboxStep(params) {
|
|
|
38
37
|
if (exists.exitCode === 0) {
|
|
39
38
|
return { sandboxId, localPath };
|
|
40
39
|
}
|
|
41
|
-
const storyRuntime = await
|
|
40
|
+
const storyRuntime = await getThreadRuntime(params.env);
|
|
42
41
|
const db = storyRuntime.db;
|
|
43
42
|
const contextKey = `structure:${params.structureId}`;
|
|
44
|
-
const
|
|
45
|
-
|
|
43
|
+
const query = (await db.query({
|
|
44
|
+
thread_contexts: {
|
|
45
|
+
$: { where: { key: contextKey }, limit: 1 },
|
|
46
|
+
structure_output_file: {},
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
const ctx = query.thread_contexts?.[0];
|
|
46
50
|
const linked = Array.isArray(ctx?.structure_output_file) ? ctx.structure_output_file[0] : ctx.structure_output_file;
|
|
47
51
|
const url = linked?.url;
|
|
48
52
|
if (!url) {
|
package/dist/rowsOutputSplit.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getDatasetOutputPath, getDatasetWorkstation } from "./datasetFiles.js";
|
|
2
2
|
import { readDatasetSandboxFileStep, runDatasetSandboxCommandStep } from "./sandbox/steps.js";
|
|
3
|
-
import {
|
|
4
|
-
import { getContextRuntime } from "./runtime.js";
|
|
3
|
+
import { getThreadRuntime } from "./runtime.js";
|
|
5
4
|
/**
|
|
6
5
|
* Step:
|
|
7
6
|
* Split a sandbox-local `output.jsonl` into a child dataset (also `output.jsonl`) of up to `limit` ROW entries.
|
|
@@ -85,7 +84,7 @@ export async function structureSplitRowsOutputToDatasetStep(params) {
|
|
|
85
84
|
sandboxId: params.sandboxId,
|
|
86
85
|
path: outPath,
|
|
87
86
|
});
|
|
88
|
-
const storyRuntime = await
|
|
87
|
+
const storyRuntime = await getThreadRuntime(params.env);
|
|
89
88
|
const db = storyRuntime.db;
|
|
90
89
|
const store = storyRuntime.store;
|
|
91
90
|
const storagePath = `/structure/${params.childDatasetId}/output.jsonl`;
|
|
@@ -103,7 +102,7 @@ export async function structureSplitRowsOutputToDatasetStep(params) {
|
|
|
103
102
|
if (!ctxId)
|
|
104
103
|
throw new Error("Failed to create child dataset context");
|
|
105
104
|
// Link the output file to the context (used by DatasetService.readRecordsFromFile).
|
|
106
|
-
await
|
|
105
|
+
await db.transact([db.tx.thread_contexts[ctxId].link({ structure_output_file: fileId })]);
|
|
107
106
|
// Patch metadata under `structure` namespace (never clobber Story runtime keys).
|
|
108
107
|
const existingContent = (ctx?.content ?? {});
|
|
109
108
|
const existingStructure = (existingContent?.structure ?? {});
|
package/dist/runtime.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Why dynamic import?
|
|
5
5
|
* - Some bundlers (notably Turbopack step bundles) can drop/hoist static imports in "use-step" modules,
|
|
6
|
-
* causing `ReferenceError:
|
|
6
|
+
* causing `ReferenceError: getThreadRuntime is not defined`.
|
|
7
7
|
* - Using a dynamic import keeps the symbol resolution local to the step runtime.
|
|
8
8
|
*/
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function getThreadRuntime(env: any): Promise<import("@ekairos/thread/runtime").ThreadRuntime>;
|
package/dist/runtime.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Why dynamic import?
|
|
5
5
|
* - Some bundlers (notably Turbopack step bundles) can drop/hoist static imports in "use-step" modules,
|
|
6
|
-
* causing `ReferenceError:
|
|
6
|
+
* causing `ReferenceError: getThreadRuntime is not defined`.
|
|
7
7
|
* - Using a dynamic import keeps the symbol resolution local to the step runtime.
|
|
8
8
|
*/
|
|
9
|
-
export async function
|
|
10
|
-
const {
|
|
11
|
-
return await
|
|
9
|
+
export async function getThreadRuntime(env) {
|
|
10
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
11
|
+
return await getThreadRuntime(env);
|
|
12
12
|
}
|
package/dist/sandbox/steps.js
CHANGED
|
@@ -104,8 +104,8 @@ export async function createDatasetSandboxStep(params) {
|
|
|
104
104
|
"use step";
|
|
105
105
|
const startedAt = Date.now();
|
|
106
106
|
const { env, ...configInput } = params;
|
|
107
|
-
const {
|
|
108
|
-
const db = (await
|
|
107
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
108
|
+
const db = (await getThreadRuntime(env)).db;
|
|
109
109
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
110
110
|
const service = new SandboxService(db);
|
|
111
111
|
const daytonaDefaults = getStructureDaytonaDefaults();
|
|
@@ -181,8 +181,8 @@ export async function createDatasetSandboxStep(params) {
|
|
|
181
181
|
export async function runDatasetSandboxCommandStep(params) {
|
|
182
182
|
"use step";
|
|
183
183
|
const startedAt = Date.now();
|
|
184
|
-
const {
|
|
185
|
-
const db = (await
|
|
184
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
185
|
+
const db = (await getThreadRuntime(params.env)).db;
|
|
186
186
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
187
187
|
const service = new SandboxService(db);
|
|
188
188
|
const result = await service.runCommand(params.sandboxId, params.cmd, params.args ?? []);
|
|
@@ -202,8 +202,8 @@ export async function runDatasetSandboxCommandStep(params) {
|
|
|
202
202
|
export async function writeDatasetSandboxFilesStep(params) {
|
|
203
203
|
"use step";
|
|
204
204
|
const startedAt = Date.now();
|
|
205
|
-
const {
|
|
206
|
-
const db = (await
|
|
205
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
206
|
+
const db = (await getThreadRuntime(params.env)).db;
|
|
207
207
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
208
208
|
const service = new SandboxService(db);
|
|
209
209
|
const result = await service.writeFiles(params.sandboxId, params.files);
|
|
@@ -233,8 +233,8 @@ export async function writeDatasetSandboxTextFileStep(params) {
|
|
|
233
233
|
export async function readDatasetSandboxFileStep(params) {
|
|
234
234
|
"use step";
|
|
235
235
|
const startedAt = Date.now();
|
|
236
|
-
const {
|
|
237
|
-
const db = (await
|
|
236
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
237
|
+
const db = (await getThreadRuntime(params.env)).db;
|
|
238
238
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
239
239
|
const service = new SandboxService(db);
|
|
240
240
|
const result = await service.readFile(params.sandboxId, params.path);
|
|
@@ -262,8 +262,8 @@ export async function readDatasetSandboxTextFileStep(params) {
|
|
|
262
262
|
export async function stopDatasetSandboxStep(params) {
|
|
263
263
|
"use step";
|
|
264
264
|
const startedAt = Date.now();
|
|
265
|
-
const {
|
|
266
|
-
const db = (await
|
|
265
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
266
|
+
const db = (await getThreadRuntime(params.env)).db;
|
|
267
267
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
268
268
|
const service = new SandboxService(db);
|
|
269
269
|
const result = await service.stopSandbox(params.sandboxId);
|
package/dist/schema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i } from "@instantdb/core";
|
|
2
2
|
import { domain } from "@ekairos/domain";
|
|
3
|
-
import {
|
|
3
|
+
import { threadDomain } from "@ekairos/thread/schema";
|
|
4
4
|
import { sandboxDomain } from "@ekairos/sandbox/schema";
|
|
5
5
|
const entities = {
|
|
6
6
|
// Keep $files compatible with Instant's base file fields used by structure flows.
|
|
@@ -19,16 +19,16 @@ const links = {
|
|
|
19
19
|
/**
|
|
20
20
|
* Structure output link (rows):
|
|
21
21
|
*
|
|
22
|
-
* - `
|
|
22
|
+
* - `thread_contexts.structure_output_file` points to the `$files` record for `output.jsonl`.
|
|
23
23
|
* - Reverse label is prefixed to avoid collisions across domains.
|
|
24
24
|
*/
|
|
25
25
|
structureContextOutputFile: {
|
|
26
|
-
forward: { on: "
|
|
26
|
+
forward: { on: "thread_contexts", has: "one", label: "structure_output_file" },
|
|
27
27
|
reverse: { on: "$files", has: "many", label: "structure_contexts" },
|
|
28
28
|
},
|
|
29
29
|
};
|
|
30
30
|
const rooms = {};
|
|
31
31
|
export const structureDomain = domain("structure")
|
|
32
|
-
.includes(
|
|
32
|
+
.includes(threadDomain)
|
|
33
33
|
.includes(sandboxDomain)
|
|
34
34
|
.schema({ entities, links, rooms });
|
package/dist/service.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type ServiceResult<T = any> = {
|
|
|
8
8
|
/**
|
|
9
9
|
* Back-compat helper for reading structure outputs outside the workflow runtime.
|
|
10
10
|
*
|
|
11
|
-
* IMPORTANT: The source of truth is
|
|
11
|
+
* IMPORTANT: The source of truth is `thread_contexts` (Story context) keyed by `structure:<id>`.
|
|
12
12
|
*/
|
|
13
13
|
export declare class DatasetService {
|
|
14
14
|
private readonly db;
|
package/dist/service.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { createStructureContext, findStructureContextByKey, linkStructureOutputFileToContextByKey, } from "./contextPersistence.js";
|
|
2
1
|
/**
|
|
3
2
|
* Back-compat helper for reading structure outputs outside the workflow runtime.
|
|
4
3
|
*
|
|
5
|
-
* IMPORTANT: The source of truth is
|
|
4
|
+
* IMPORTANT: The source of truth is `thread_contexts` (Story context) keyed by `structure:<id>`.
|
|
6
5
|
*/
|
|
7
6
|
export class DatasetService {
|
|
8
7
|
constructor(db) {
|
|
@@ -14,8 +13,13 @@ export class DatasetService {
|
|
|
14
13
|
async getDatasetById(datasetId) {
|
|
15
14
|
try {
|
|
16
15
|
const key = this.contextKey(datasetId);
|
|
17
|
-
const
|
|
18
|
-
|
|
16
|
+
const res = await this.db.query({
|
|
17
|
+
thread_contexts: {
|
|
18
|
+
$: { where: { key }, limit: 1 },
|
|
19
|
+
structure_output_file: {},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const ctx = res.thread_contexts?.[0];
|
|
19
23
|
if (!ctx)
|
|
20
24
|
return { ok: false, error: "Context not found" };
|
|
21
25
|
return { ok: true, data: ctx };
|
|
@@ -39,8 +43,13 @@ export class DatasetService {
|
|
|
39
43
|
async readRecordsFromFile(datasetId) {
|
|
40
44
|
try {
|
|
41
45
|
const key = this.contextKey(datasetId);
|
|
42
|
-
const
|
|
43
|
-
|
|
46
|
+
const res = await this.db.query({
|
|
47
|
+
thread_contexts: {
|
|
48
|
+
$: { where: { key }, limit: 1 },
|
|
49
|
+
structure_output_file: {},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const ctx = res.thread_contexts?.[0];
|
|
44
53
|
const linked = Array.isArray(ctx?.structure_output_file) ? ctx.structure_output_file[0] : ctx?.structure_output_file;
|
|
45
54
|
const url = linked?.url;
|
|
46
55
|
if (!url)
|
|
@@ -80,14 +89,20 @@ export class DatasetService {
|
|
|
80
89
|
try {
|
|
81
90
|
const datasetId = params.id ?? createUuidV4();
|
|
82
91
|
const key = this.contextKey(datasetId);
|
|
83
|
-
const existing = await
|
|
84
|
-
|
|
92
|
+
const existing = await this.db.query({
|
|
93
|
+
thread_contexts: { $: { where: { key }, limit: 1 } },
|
|
94
|
+
});
|
|
95
|
+
const ctx = existing.thread_contexts?.[0];
|
|
85
96
|
if (ctx)
|
|
86
97
|
return { ok: true, data: { datasetId } };
|
|
87
|
-
await
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
await this.db.transact([
|
|
99
|
+
this.db.tx.thread_contexts[createUuidV4()].create({
|
|
100
|
+
createdAt: new Date(),
|
|
101
|
+
content: {},
|
|
102
|
+
key,
|
|
103
|
+
status: "open",
|
|
104
|
+
}),
|
|
105
|
+
]);
|
|
91
106
|
return { ok: true, data: { datasetId } };
|
|
92
107
|
}
|
|
93
108
|
catch (error) {
|
|
@@ -118,10 +133,14 @@ export class DatasetService {
|
|
|
118
133
|
async linkFileToDataset(params) {
|
|
119
134
|
try {
|
|
120
135
|
const key = this.contextKey(params.datasetId);
|
|
121
|
-
const
|
|
122
|
-
|
|
136
|
+
const res = await this.db.query({
|
|
137
|
+
thread_contexts: { $: { where: { key }, limit: 1 } },
|
|
138
|
+
});
|
|
139
|
+
const ctx = res?.thread_contexts?.[0];
|
|
140
|
+
const ctxId = ctx?.id;
|
|
141
|
+
if (!ctxId)
|
|
123
142
|
return { ok: false, error: "Context not found" };
|
|
124
|
-
await
|
|
143
|
+
await this.db.transact([this.db.tx.thread_contexts[ctxId].link({ structure_output_file: params.fileId })]);
|
|
125
144
|
return { ok: true, data: undefined };
|
|
126
145
|
}
|
|
127
146
|
catch (error) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { linkStructureOutputFileToContextByKey } from "../contextPersistence.js";
|
|
2
1
|
function isToolCompletePart(value) {
|
|
3
2
|
if (!value || typeof value !== "object")
|
|
4
3
|
return false;
|
|
@@ -23,8 +22,8 @@ export async function structureCommitFromEventsStep(params) {
|
|
|
23
22
|
"use step";
|
|
24
23
|
const contextKey = `structure:${params.structureId}`;
|
|
25
24
|
try {
|
|
26
|
-
const {
|
|
27
|
-
const runtime = (await
|
|
25
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
26
|
+
const runtime = (await getThreadRuntime(params.env));
|
|
28
27
|
const store = runtime.store;
|
|
29
28
|
const db = runtime.db;
|
|
30
29
|
const events = await store.getItems({ key: contextKey });
|
|
@@ -67,9 +66,10 @@ export async function structureCommitFromEventsStep(params) {
|
|
|
67
66
|
},
|
|
68
67
|
};
|
|
69
68
|
// Link output file to context (domain-prefixed link)
|
|
70
|
-
|
|
69
|
+
const ctxId = ctx?.id;
|
|
70
|
+
if (!ctxId)
|
|
71
71
|
return { ok: false, error: "Context not found" };
|
|
72
|
-
await
|
|
72
|
+
await db.transact(db.tx.thread_contexts[ctxId].link({ structure_output_file: out.fileId }));
|
|
73
73
|
}
|
|
74
74
|
await store.updateContextContent({ key: contextKey }, { ...prevContent, structure: nextStructure });
|
|
75
75
|
return { ok: true, data: { committed: true } };
|
|
@@ -36,8 +36,8 @@ function extractJsonObject(text) {
|
|
|
36
36
|
}
|
|
37
37
|
export async function persistObjectResultFromStoryStep(params) {
|
|
38
38
|
"use step";
|
|
39
|
-
const {
|
|
40
|
-
const runtime = (await
|
|
39
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
40
|
+
const runtime = (await getThreadRuntime(params.env));
|
|
41
41
|
const store = runtime.store;
|
|
42
42
|
const contextKey = `structure:${params.datasetId}`;
|
|
43
43
|
const events = await store.getEvents({ key: contextKey });
|
package/dist/structure.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createThread, didToolExecute, INPUT_TEXT_ITEM_TYPE, WEB_CHANNEL } from "@ekairos/thread";
|
|
2
2
|
import { getDatasetOutputPath, getDatasetOutputSchemaPath, getDatasetWorkstation, getDaytonaVolumeMountPath, getDaytonaVolumeName, } from "./datasetFiles.js";
|
|
3
3
|
import { structureDownloadRowsOutputToSandboxStep, structureReadRowsOutputPageFromSandboxStep, } from "./rowsOutputPaging.js";
|
|
4
4
|
import { structureSplitRowsOutputToDatasetStep } from "./rowsOutputSplit.js";
|
|
5
5
|
import { createDatasetSandboxStep, readDatasetSandboxFileStep, readDatasetSandboxTextFileStep, runDatasetSandboxCommandStep, writeDatasetSandboxFilesStep, writeDatasetSandboxTextFileStep, } from "./sandbox/steps.js";
|
|
6
6
|
import { readInstantFileStep } from "./file/steps.js";
|
|
7
|
-
import { structureGetContextStep, structureGetContextWithRowsOutputFileStep,
|
|
7
|
+
import { structureGetContextStep, structureGetContextWithRowsOutputFileStep, structureReadRowsOutputJsonlStep, } from "./dataset/steps.js";
|
|
8
8
|
import { getWorkflowMetadata } from "workflow";
|
|
9
9
|
import { buildStructurePrompt } from "./prompts.js";
|
|
10
10
|
import { createExecuteCommandTool } from "./executeCommand.tool.js";
|
|
@@ -14,7 +14,6 @@ import { createCompleteRowsTool } from "./completeRows.tool.js";
|
|
|
14
14
|
import { createCompleteObjectTool } from "./completeObject.tool.js";
|
|
15
15
|
import { persistObjectResultFromStoryStep } from "./steps/persistObjectFromStory.step.js";
|
|
16
16
|
import { structureCommitFromEventsStep } from "./steps/commitFromEvents.step.js";
|
|
17
|
-
import { ensureExecutionTrailStep } from "./steps/ensureExecutionTrail.step.js";
|
|
18
17
|
function createUuidV4() {
|
|
19
18
|
// Pure JS uuidv4 (workflow-safe: avoids Node built-ins and @instantdb/admin)
|
|
20
19
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
@@ -23,20 +22,6 @@ function createUuidV4() {
|
|
|
23
22
|
return v.toString(16);
|
|
24
23
|
});
|
|
25
24
|
}
|
|
26
|
-
function toSerializableStructureError(error) {
|
|
27
|
-
if (!error)
|
|
28
|
-
return undefined;
|
|
29
|
-
if (typeof error === "string")
|
|
30
|
-
return error;
|
|
31
|
-
if (error instanceof Error) {
|
|
32
|
-
return {
|
|
33
|
-
name: error.name,
|
|
34
|
-
message: error.message,
|
|
35
|
-
stack: error.stack ? String(error.stack).slice(0, 2000) : undefined,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
return String(error);
|
|
39
|
-
}
|
|
40
25
|
function assertRunningInsideWorkflow(params) {
|
|
41
26
|
const bypassRaw = String(process.env.STRUCTURE_ALLOW_NON_WORKFLOW ?? "")
|
|
42
27
|
.trim()
|
|
@@ -281,7 +266,7 @@ function createStructureStoryDefinition(config) {
|
|
|
281
266
|
const model = config.model ?? "openai/gpt-5.2";
|
|
282
267
|
const defaultSandboxConfig = getDefaultSandboxConfig(datasetId);
|
|
283
268
|
const resolvedSandboxConfig = mergeSandboxConfig(defaultSandboxConfig, config.sandboxConfig);
|
|
284
|
-
const story =
|
|
269
|
+
const story = createThread("ekairos.structure")
|
|
285
270
|
.context(async (stored, env) => {
|
|
286
271
|
const prev = stored?.content ?? {};
|
|
287
272
|
const sandboxState = prev.sandboxState ?? { initialized: false, sources: [] };
|
|
@@ -470,30 +455,6 @@ export function structure(env, opts) {
|
|
|
470
455
|
content: { parts: [{ type: "text", text }] },
|
|
471
456
|
};
|
|
472
457
|
}
|
|
473
|
-
async function markBuildFailed(error) {
|
|
474
|
-
try {
|
|
475
|
-
await structurePatchContextContentStep({
|
|
476
|
-
env,
|
|
477
|
-
contextKey,
|
|
478
|
-
patch: {
|
|
479
|
-
structure: {
|
|
480
|
-
kind: "ekairos.structure",
|
|
481
|
-
version: 1,
|
|
482
|
-
structureId: datasetId,
|
|
483
|
-
updatedAt: Date.now(),
|
|
484
|
-
state: "failed",
|
|
485
|
-
error: {
|
|
486
|
-
message: error instanceof Error ? error.message : String(error),
|
|
487
|
-
stage: "structure.build",
|
|
488
|
-
},
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
catch {
|
|
494
|
-
// keep failure explicit even if persistence is partially unavailable
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
458
|
async function runStory(evt) {
|
|
498
459
|
await story.react(evt, {
|
|
499
460
|
env,
|
|
@@ -503,7 +464,6 @@ export function structure(env, opts) {
|
|
|
503
464
|
// Tools are intentionally pure: persist completion outputs post-react by reading tool results from events.
|
|
504
465
|
const commit = await structureCommitFromEventsStep({ env, structureId: datasetId });
|
|
505
466
|
if (!commit.ok) {
|
|
506
|
-
throw new Error(commit.error);
|
|
507
467
|
}
|
|
508
468
|
}
|
|
509
469
|
async function getContextOrThrow() {
|
|
@@ -525,137 +485,115 @@ export function structure(env, opts) {
|
|
|
525
485
|
return (content?.structure?.outputs?.object?.value !== undefined &&
|
|
526
486
|
content?.structure?.outputs?.object?.value !== null);
|
|
527
487
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
"3) After schema is saved, produce the final output and call complete.",
|
|
542
|
-
].join("\n")));
|
|
543
|
-
ctx = await getContextOrThrow();
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
const needsSecondPass = output === "rows" ? !(await isRowsCompleted()) : !isObjectCompleted(ctx);
|
|
547
|
-
if (needsSecondPass) {
|
|
548
|
-
const followUpText = output === "rows"
|
|
549
|
-
? "Finalize now: write output.jsonl to OutputPath and call complete."
|
|
550
|
-
: "Finalize now: call complete with summary and resultJson (inline JSON).";
|
|
551
|
-
await runStory(makeUserMessageEvent(followUpText));
|
|
488
|
+
await runStory(makeUserMessageEvent(userPrompt ?? "produce structured output"));
|
|
489
|
+
let ctx = await getContextOrThrow();
|
|
490
|
+
// Auto-mode: if schema is missing after the first pass, explicitly request investigation + generateSchema.
|
|
491
|
+
if (mode === "auto") {
|
|
492
|
+
const content = (ctx?.content ?? {});
|
|
493
|
+
const hasSchema = Boolean(content?.structure?.outputSchema?.schema);
|
|
494
|
+
if (!hasSchema) {
|
|
495
|
+
await runStory(makeUserMessageEvent([
|
|
496
|
+
"CRITICAL: You did not generate a schema yet.",
|
|
497
|
+
"1) Investigate Sources using executeCommand (inspect paths, read files, infer structure).",
|
|
498
|
+
"2) Call generateSchema.",
|
|
499
|
+
"3) After schema is saved, produce the final output and call complete.",
|
|
500
|
+
].join("\n")));
|
|
552
501
|
ctx = await getContextOrThrow();
|
|
553
502
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
503
|
+
}
|
|
504
|
+
const needsSecondPass = output === "rows" ? !(await isRowsCompleted()) : !isObjectCompleted(ctx);
|
|
505
|
+
if (needsSecondPass) {
|
|
506
|
+
const followUpText = output === "rows"
|
|
507
|
+
? "Finalize now: write output.jsonl to OutputPath and call complete."
|
|
508
|
+
: "Finalize now: call complete with summary and resultJson (inline JSON).";
|
|
509
|
+
await runStory(makeUserMessageEvent(followUpText));
|
|
510
|
+
ctx = await getContextOrThrow();
|
|
511
|
+
}
|
|
512
|
+
const stillIncompleteAfterSecondPass = output === "rows" ? !(await isRowsCompleted()) : !isObjectCompleted(ctx);
|
|
513
|
+
if (stillIncompleteAfterSecondPass) {
|
|
514
|
+
const finalText = output === "rows"
|
|
515
|
+
? "CRITICAL: Do not do anything else. Ensure output.jsonl exists at OutputPath and immediately call complete."
|
|
516
|
+
: "CRITICAL: Do not do anything else. Immediately call complete with summary and resultJson (inline JSON).";
|
|
517
|
+
await runStory(makeUserMessageEvent(finalText));
|
|
518
|
+
ctx = await getContextOrThrow();
|
|
519
|
+
}
|
|
520
|
+
if (output === "rows" && !(await isRowsCompleted())) {
|
|
521
|
+
throw new Error("Rows output not completed");
|
|
522
|
+
}
|
|
523
|
+
if (output === "object" && !isObjectCompleted(ctx)) {
|
|
524
|
+
const persisted = await persistObjectResultFromStoryStep({ env, datasetId });
|
|
525
|
+
if (persisted.ok) {
|
|
560
526
|
ctx = await getContextOrThrow();
|
|
561
527
|
}
|
|
562
|
-
|
|
563
|
-
throw new Error("Rows output not completed");
|
|
528
|
+
else {
|
|
564
529
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
530
|
+
}
|
|
531
|
+
if (output === "object" && !isObjectCompleted(ctx)) {
|
|
532
|
+
throw new Error("Object output not completed");
|
|
533
|
+
}
|
|
534
|
+
let rowsSandboxRef = null;
|
|
535
|
+
const reader = {
|
|
536
|
+
read: async (cursorOrParams, limit) => {
|
|
537
|
+
if (output !== "rows") {
|
|
538
|
+
throw new Error("reader.read() is only supported for output=rows");
|
|
569
539
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
throw new Error("Object output not completed");
|
|
573
|
-
}
|
|
574
|
-
await ensureExecutionTrailStep({
|
|
575
|
-
env,
|
|
576
|
-
contextKey,
|
|
577
|
-
datasetId,
|
|
578
|
-
output,
|
|
579
|
-
status: "success",
|
|
580
|
-
requestItemId: initialRequestEvent.id,
|
|
581
|
-
});
|
|
582
|
-
let rowsSandboxRef = null;
|
|
583
|
-
const reader = {
|
|
584
|
-
read: async (cursorOrParams, limit) => {
|
|
585
|
-
if (output !== "rows") {
|
|
586
|
-
throw new Error("reader.read() is only supported for output=rows");
|
|
587
|
-
}
|
|
588
|
-
if (!rowsSandboxRef) {
|
|
589
|
-
rowsSandboxRef = await structureDownloadRowsOutputToSandboxStep({
|
|
590
|
-
env,
|
|
591
|
-
structureId: datasetId,
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
const params = cursorOrParams && typeof cursorOrParams === "object" && ("cursor" in cursorOrParams || "limit" in cursorOrParams)
|
|
595
|
-
? cursorOrParams
|
|
596
|
-
: { cursor: cursorOrParams, limit };
|
|
597
|
-
const page = await structureReadRowsOutputPageFromSandboxStep({
|
|
540
|
+
if (!rowsSandboxRef) {
|
|
541
|
+
rowsSandboxRef = await structureDownloadRowsOutputToSandboxStep({
|
|
598
542
|
env,
|
|
599
|
-
|
|
600
|
-
localPath: rowsSandboxRef.localPath,
|
|
601
|
-
cursor: params?.cursor,
|
|
602
|
-
limit: params?.limit ?? 200,
|
|
543
|
+
structureId: datasetId,
|
|
603
544
|
});
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const childDatasetId = params?.datasetId ?? createUuidV4();
|
|
628
|
-
const res = await structureSplitRowsOutputToDatasetStep({
|
|
545
|
+
}
|
|
546
|
+
const params = cursorOrParams && typeof cursorOrParams === "object" && ("cursor" in cursorOrParams || "limit" in cursorOrParams)
|
|
547
|
+
? cursorOrParams
|
|
548
|
+
: { cursor: cursorOrParams, limit };
|
|
549
|
+
const page = await structureReadRowsOutputPageFromSandboxStep({
|
|
550
|
+
env,
|
|
551
|
+
sandboxId: rowsSandboxRef.sandboxId,
|
|
552
|
+
localPath: rowsSandboxRef.localPath,
|
|
553
|
+
cursor: params?.cursor,
|
|
554
|
+
limit: params?.limit ?? 200,
|
|
555
|
+
});
|
|
556
|
+
return {
|
|
557
|
+
rows: page.rows,
|
|
558
|
+
cursor: page.nextCursor,
|
|
559
|
+
done: page.done,
|
|
560
|
+
};
|
|
561
|
+
},
|
|
562
|
+
split: async (cursorOrParams, limit) => {
|
|
563
|
+
if (output !== "rows") {
|
|
564
|
+
throw new Error("reader.split() is only supported for output=rows");
|
|
565
|
+
}
|
|
566
|
+
if (!rowsSandboxRef) {
|
|
567
|
+
rowsSandboxRef = await structureDownloadRowsOutputToSandboxStep({
|
|
629
568
|
env,
|
|
630
|
-
|
|
631
|
-
localPath: rowsSandboxRef.localPath,
|
|
632
|
-
cursor: params?.cursor,
|
|
633
|
-
limit: params?.limit ?? 300,
|
|
634
|
-
childDatasetId,
|
|
569
|
+
structureId: datasetId,
|
|
635
570
|
});
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
571
|
+
}
|
|
572
|
+
const params = cursorOrParams && typeof cursorOrParams === "object" && ("cursor" in cursorOrParams || "limit" in cursorOrParams)
|
|
573
|
+
? cursorOrParams
|
|
574
|
+
: {
|
|
575
|
+
cursor: cursorOrParams,
|
|
576
|
+
limit,
|
|
577
|
+
datasetId: undefined,
|
|
641
578
|
};
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}
|
|
579
|
+
const childDatasetId = params?.datasetId ?? createUuidV4();
|
|
580
|
+
const res = await structureSplitRowsOutputToDatasetStep({
|
|
581
|
+
env,
|
|
582
|
+
sandboxId: rowsSandboxRef.sandboxId,
|
|
583
|
+
localPath: rowsSandboxRef.localPath,
|
|
584
|
+
cursor: params?.cursor,
|
|
585
|
+
limit: params?.limit ?? 300,
|
|
586
|
+
childDatasetId,
|
|
587
|
+
});
|
|
588
|
+
return {
|
|
589
|
+
datasetId: res.datasetId,
|
|
590
|
+
rowsWritten: res.rowsWritten,
|
|
591
|
+
cursor: res.nextCursor,
|
|
592
|
+
done: res.done,
|
|
593
|
+
};
|
|
594
|
+
},
|
|
595
|
+
};
|
|
596
|
+
return output === "object" ? { datasetId, reader, dataset: ctx } : { datasetId, reader };
|
|
659
597
|
},
|
|
660
598
|
};
|
|
661
599
|
return api;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekairos/structure",
|
|
3
|
-
"version": "1.22.4-beta.
|
|
3
|
+
"version": "1.22.4-beta.feature-thread-unify.0",
|
|
4
4
|
"description": "Ekairos Structure - Unified structured extraction (rows or object) from file/text/dataset inputs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"typecheck": "tsc --noEmit"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ekairos/domain": "^1.22.4-beta.
|
|
40
|
-
"@ekairos/sandbox": "^1.22.4-beta.
|
|
39
|
+
"@ekairos/domain": "^1.22.4-beta.feature-thread-unify.0",
|
|
40
|
+
"@ekairos/sandbox": "^1.22.4-beta.feature-thread-unify.0",
|
|
41
41
|
"@instantdb/admin": "0.22.126",
|
|
42
42
|
"@instantdb/core": "0.22.126",
|
|
43
43
|
"ai": "^5.0.95",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"zod": "^4.1.8"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@ekairos/
|
|
49
|
+
"@ekairos/thread": "workspace:*"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@ekairos/sandbox": "workspace:*",
|
|
53
|
-
"@ekairos/
|
|
53
|
+
"@ekairos/thread": "workspace:*",
|
|
54
54
|
"@ekairos/tsconfig": "workspace:*",
|
|
55
55
|
"@types/node": "^24.5.0",
|
|
56
56
|
"dotenv": "^17.2.2",
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
declare const STRUCTURE_CONTEXT_ENTITIES: readonly ["event_contexts"];
|
|
2
|
-
type StructureContextEntity = (typeof STRUCTURE_CONTEXT_ENTITIES)[number];
|
|
3
|
-
type FindStructureContextParams = {
|
|
4
|
-
includeOutputFile?: boolean;
|
|
5
|
-
};
|
|
6
|
-
type FindStructureContextResult = {
|
|
7
|
-
entity: StructureContextEntity;
|
|
8
|
-
row: any;
|
|
9
|
-
};
|
|
10
|
-
export declare function findStructureContextByKey(db: any, key: string, params?: FindStructureContextParams): Promise<FindStructureContextResult | null>;
|
|
11
|
-
export declare function createStructureContext(db: any, params: {
|
|
12
|
-
id: string;
|
|
13
|
-
key: string;
|
|
14
|
-
content?: Record<string, unknown>;
|
|
15
|
-
status?: string;
|
|
16
|
-
createdAt?: Date;
|
|
17
|
-
}): Promise<{
|
|
18
|
-
entity: "event_contexts";
|
|
19
|
-
id: string;
|
|
20
|
-
}>;
|
|
21
|
-
export declare function linkStructureOutputFileToContextByKey(db: any, params: {
|
|
22
|
-
contextKey: string;
|
|
23
|
-
fileId: string;
|
|
24
|
-
}): Promise<void>;
|
|
25
|
-
export declare function unlinkStructureOutputFileFromContextByKey(db: any, params: {
|
|
26
|
-
contextKey: string;
|
|
27
|
-
fileId: string;
|
|
28
|
-
}): Promise<void>;
|
|
29
|
-
export {};
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const STRUCTURE_CONTEXT_ENTITIES = ["event_contexts"];
|
|
2
|
-
function buildContextQuery(entity, key, includeOutputFile) {
|
|
3
|
-
return {
|
|
4
|
-
[entity]: {
|
|
5
|
-
$: { where: { key }, limit: 1 },
|
|
6
|
-
...(includeOutputFile ? { structure_output_file: {} } : {}),
|
|
7
|
-
},
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
function preferredContextEntity(db) {
|
|
11
|
-
if (db?.tx?.event_contexts)
|
|
12
|
-
return "event_contexts";
|
|
13
|
-
throw new Error("No persisted context collection is available");
|
|
14
|
-
}
|
|
15
|
-
export async function findStructureContextByKey(db, key, params = {}) {
|
|
16
|
-
let lastError = null;
|
|
17
|
-
let queried = false;
|
|
18
|
-
for (const entity of STRUCTURE_CONTEXT_ENTITIES) {
|
|
19
|
-
try {
|
|
20
|
-
const res = await db.query(buildContextQuery(entity, key, Boolean(params.includeOutputFile)));
|
|
21
|
-
queried = true;
|
|
22
|
-
const row = res?.[entity]?.[0];
|
|
23
|
-
if (row)
|
|
24
|
-
return { entity, row };
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
lastError = error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (!queried && lastError) {
|
|
31
|
-
throw lastError;
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
export async function createStructureContext(db, params) {
|
|
36
|
-
const entity = preferredContextEntity(db);
|
|
37
|
-
await db.transact([
|
|
38
|
-
db.tx[entity][params.id].create({
|
|
39
|
-
createdAt: params.createdAt ?? new Date(),
|
|
40
|
-
content: params.content ?? {},
|
|
41
|
-
key: params.key,
|
|
42
|
-
status: params.status ?? "open",
|
|
43
|
-
}),
|
|
44
|
-
]);
|
|
45
|
-
return { entity, id: params.id };
|
|
46
|
-
}
|
|
47
|
-
export async function linkStructureOutputFileToContextByKey(db, params) {
|
|
48
|
-
const context = await findStructureContextByKey(db, params.contextKey);
|
|
49
|
-
const ctxId = context?.row?.id;
|
|
50
|
-
if (!context || !ctxId) {
|
|
51
|
-
throw new Error("Context not found");
|
|
52
|
-
}
|
|
53
|
-
await db.transact([db.tx[context.entity][ctxId].link({ structure_output_file: params.fileId })]);
|
|
54
|
-
}
|
|
55
|
-
export async function unlinkStructureOutputFileFromContextByKey(db, params) {
|
|
56
|
-
const context = await findStructureContextByKey(db, params.contextKey);
|
|
57
|
-
const ctxId = context?.row?.id;
|
|
58
|
-
if (!context || !ctxId) {
|
|
59
|
-
throw new Error("Context not found");
|
|
60
|
-
}
|
|
61
|
-
await db.transact([db.tx[context.entity][ctxId].unlink({ structure_output_file: params.fileId })]);
|
|
62
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
type EnsureExecutionTrailParams = {
|
|
2
|
-
env: any;
|
|
3
|
-
contextKey: string;
|
|
4
|
-
datasetId: string;
|
|
5
|
-
output: "rows" | "object";
|
|
6
|
-
requestItemId: string;
|
|
7
|
-
status: "success" | "failed";
|
|
8
|
-
error?: unknown;
|
|
9
|
-
};
|
|
10
|
-
export declare function ensureExecutionTrailStep(params: EnsureExecutionTrailParams): Promise<void>;
|
|
11
|
-
export {};
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
function createTraceItemId(seed) {
|
|
2
|
-
let hashA = 2166136261;
|
|
3
|
-
let hashB = 2166136261;
|
|
4
|
-
for (let i = 0; i < seed.length; i += 1) {
|
|
5
|
-
const code = seed.charCodeAt(i);
|
|
6
|
-
hashA ^= code;
|
|
7
|
-
hashA = Math.imul(hashA, 16777619);
|
|
8
|
-
hashB ^= code + (i % 13);
|
|
9
|
-
hashB = Math.imul(hashB, 16777619);
|
|
10
|
-
}
|
|
11
|
-
const partA = (hashA >>> 0).toString(16).padStart(8, "0");
|
|
12
|
-
const partB = (hashB >>> 0).toString(16).padStart(8, "0");
|
|
13
|
-
const hex = `${partA}${partB}${partA}${partB}`;
|
|
14
|
-
const chars = hex.slice(0, 32).split("");
|
|
15
|
-
chars[12] = "4";
|
|
16
|
-
const variant = parseInt(chars[16], 16);
|
|
17
|
-
chars[16] = ((variant & 0x3) | 0x8).toString(16);
|
|
18
|
-
const normalized = chars.join("");
|
|
19
|
-
return `${normalized.slice(0, 8)}-${normalized.slice(8, 12)}-${normalized.slice(12, 16)}-${normalized.slice(16, 20)}-${normalized.slice(20, 32)}`;
|
|
20
|
-
}
|
|
21
|
-
function toSerializableError(error) {
|
|
22
|
-
if (!error)
|
|
23
|
-
return undefined;
|
|
24
|
-
if (typeof error === "string")
|
|
25
|
-
return error;
|
|
26
|
-
if (error instanceof Error) {
|
|
27
|
-
return {
|
|
28
|
-
name: error.name,
|
|
29
|
-
message: error.message,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
return String(error);
|
|
33
|
-
}
|
|
34
|
-
export async function ensureExecutionTrailStep(params) {
|
|
35
|
-
"use step";
|
|
36
|
-
const { getContextRuntime } = await import("@ekairos/events/runtime");
|
|
37
|
-
const runtime = (await getContextRuntime(params.env));
|
|
38
|
-
const store = runtime.store;
|
|
39
|
-
if (!store?.saveItem ||
|
|
40
|
-
!store?.createExecution ||
|
|
41
|
-
!store?.linkItemToExecution ||
|
|
42
|
-
!store?.getItems) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const items = await store.getItems({ key: params.contextKey });
|
|
46
|
-
const hasRequest = items.some((item) => item?.id === params.requestItemId);
|
|
47
|
-
let requestItemId = params.requestItemId;
|
|
48
|
-
if (!hasRequest) {
|
|
49
|
-
const recreatedRequestItemId = createTraceItemId(`structure-trace:request:${params.contextKey}:${params.datasetId}:${params.requestItemId}`);
|
|
50
|
-
const requestItem = await store.saveItem({ key: params.contextKey }, {
|
|
51
|
-
id: recreatedRequestItemId,
|
|
52
|
-
type: "input",
|
|
53
|
-
channel: "web",
|
|
54
|
-
createdAt: new Date().toISOString(),
|
|
55
|
-
content: {
|
|
56
|
-
parts: [{ type: "text", text: "[structure-trace] request (recreated)" }],
|
|
57
|
-
structure_build: {
|
|
58
|
-
datasetId: params.datasetId,
|
|
59
|
-
status: "input",
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
if (!requestItem?.id)
|
|
64
|
-
return;
|
|
65
|
-
requestItemId = requestItem.id;
|
|
66
|
-
}
|
|
67
|
-
const outputItemId = createTraceItemId(`structure-trace:output:${params.contextKey}:${params.datasetId}:${params.status}:${params.output}`);
|
|
68
|
-
const outputItem = await store.saveItem({ key: params.contextKey }, {
|
|
69
|
-
id: outputItemId,
|
|
70
|
-
type: "output",
|
|
71
|
-
channel: "web",
|
|
72
|
-
createdAt: new Date().toISOString(),
|
|
73
|
-
content: {
|
|
74
|
-
parts: [
|
|
75
|
-
{ type: "text", text: `structure:${params.datasetId} ${params.status}` },
|
|
76
|
-
],
|
|
77
|
-
structure_build: {
|
|
78
|
-
datasetId: params.datasetId,
|
|
79
|
-
status: params.status,
|
|
80
|
-
output: params.output,
|
|
81
|
-
orgId: params.env?.orgId,
|
|
82
|
-
error: toSerializableError(params.error),
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
if (!outputItem?.id)
|
|
87
|
-
return;
|
|
88
|
-
const execution = await store.createExecution({ key: params.contextKey }, requestItemId, outputItem.id);
|
|
89
|
-
await store.linkItemToExecution({
|
|
90
|
-
itemId: requestItemId,
|
|
91
|
-
executionId: execution.id,
|
|
92
|
-
});
|
|
93
|
-
await store.linkItemToExecution({
|
|
94
|
-
itemId: outputItem.id,
|
|
95
|
-
executionId: execution.id,
|
|
96
|
-
});
|
|
97
|
-
if (store?.completeExecution) {
|
|
98
|
-
await store.completeExecution({ key: params.contextKey }, execution.id, "completed");
|
|
99
|
-
}
|
|
100
|
-
}
|