@ekairos/structure 1.21.62-beta.0 → 1.21.69-beta.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/clearDataset.tool.js +0 -3
- package/dist/completeObject.tool.js +0 -7
- package/dist/completeRows.tool.js +0 -9
- package/dist/executeCommand.tool.js +0 -8
- package/dist/generateSchema.tool.js +0 -18
- package/dist/rowsOutputSplit.d.ts +25 -0
- package/dist/rowsOutputSplit.js +131 -0
- package/dist/sandbox/steps.js +0 -20
- package/dist/steps/persistObjectFromStory.step.js +0 -6
- package/dist/structure.d.ts +22 -0
- package/dist/structure.js +53 -56
- package/package.json +3 -2
|
@@ -9,8 +9,6 @@ export function createClearDatasetTool({ datasetId, sandboxId, env }) {
|
|
|
9
9
|
reason: z.string().describe("Reason for clearing"),
|
|
10
10
|
}),
|
|
11
11
|
execute: async ({ reason }) => {
|
|
12
|
-
console.log("[ekairos/structure] clearDataset.tool execute begin");
|
|
13
|
-
console.log("[ekairos/structure] clearDataset.tool execute datasetId", datasetId);
|
|
14
12
|
const outputPath = getDatasetOutputPath(datasetId);
|
|
15
13
|
try {
|
|
16
14
|
await runDatasetSandboxCommandStep({ env, sandboxId, cmd: "rm", args: ["-f", outputPath] });
|
|
@@ -18,7 +16,6 @@ export function createClearDatasetTool({ datasetId, sandboxId, env }) {
|
|
|
18
16
|
catch {
|
|
19
17
|
// best-effort
|
|
20
18
|
}
|
|
21
|
-
console.log("[ekairos/structure] clearDataset.tool execute ok");
|
|
22
19
|
return { success: true, message: "Cleared", reason };
|
|
23
20
|
},
|
|
24
21
|
});
|
|
@@ -24,8 +24,6 @@ export function createCompleteObjectTool({ datasetId, sandboxId, env }) {
|
|
|
24
24
|
message: "Provide result, resultJson or resultPath",
|
|
25
25
|
}),
|
|
26
26
|
execute: async (input) => {
|
|
27
|
-
console.log("[ekairos/structure] completeObject.tool execute begin");
|
|
28
|
-
console.log("[ekairos/structure] completeObject.tool execute datasetId", datasetId);
|
|
29
27
|
const contextKey = `structure:${datasetId}`;
|
|
30
28
|
const ctxResult = await structureGetContextStep({ env, contextKey });
|
|
31
29
|
if (!ctxResult.ok)
|
|
@@ -37,7 +35,6 @@ export function createCompleteObjectTool({ datasetId, sandboxId, env }) {
|
|
|
37
35
|
else {
|
|
38
36
|
let jsonText = input.resultJson ?? "";
|
|
39
37
|
if (!jsonText && input.resultPath) {
|
|
40
|
-
console.log("[ekairos/structure] completeObject.tool execute readingResultPath");
|
|
41
38
|
const fileRead = await readDatasetSandboxTextFileStep({ env, sandboxId, path: input.resultPath });
|
|
42
39
|
jsonText = fileRead.text ?? "";
|
|
43
40
|
}
|
|
@@ -46,7 +43,6 @@ export function createCompleteObjectTool({ datasetId, sandboxId, env }) {
|
|
|
46
43
|
}
|
|
47
44
|
catch (error) {
|
|
48
45
|
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
-
console.log("[ekairos/structure] completeObject.tool execute invalidJson");
|
|
50
46
|
return { success: false, error: `Invalid JSON: ${message}` };
|
|
51
47
|
}
|
|
52
48
|
}
|
|
@@ -62,17 +58,14 @@ export function createCompleteObjectTool({ datasetId, sandboxId, env }) {
|
|
|
62
58
|
const errors = Array.isArray(validator.errors)
|
|
63
59
|
? validator.errors.map((err) => err.message || "Unknown validation error")
|
|
64
60
|
: ["Unknown validation error"];
|
|
65
|
-
console.log("[ekairos/structure] completeObject.tool execute schemaValidationFailed");
|
|
66
61
|
return { success: false, error: errors.slice(0, 5).join("; ") };
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
catch (error) {
|
|
70
65
|
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
-
console.log("[ekairos/structure] completeObject.tool execute schemaValidateThrew");
|
|
72
66
|
return { success: false, error: `Failed to validate schema: ${message}` };
|
|
73
67
|
}
|
|
74
68
|
}
|
|
75
|
-
console.log("[ekairos/structure] completeObject.tool execute ok");
|
|
76
69
|
return { success: true, summary: input.summary, result: obj };
|
|
77
70
|
},
|
|
78
71
|
});
|
|
@@ -18,8 +18,6 @@ export function createCompleteRowsTool({ datasetId, sandboxId, env }) {
|
|
|
18
18
|
summary: z.string().describe("Summary of the completed dataset"),
|
|
19
19
|
}),
|
|
20
20
|
execute: async ({ summary }) => {
|
|
21
|
-
console.log("[ekairos/structure] completeRows.tool execute begin");
|
|
22
|
-
console.log("[ekairos/structure] completeRows.tool execute datasetId", datasetId);
|
|
23
21
|
const contextKey = `structure:${datasetId}`;
|
|
24
22
|
const outputPath = getDatasetOutputPath(datasetId);
|
|
25
23
|
try {
|
|
@@ -27,7 +25,6 @@ export function createCompleteRowsTool({ datasetId, sandboxId, env }) {
|
|
|
27
25
|
}
|
|
28
26
|
catch (error) {
|
|
29
27
|
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
-
console.log("[ekairos/structure] completeRows.tool execute missingOutputFile");
|
|
31
28
|
return { success: false, error: message };
|
|
32
29
|
}
|
|
33
30
|
const ctxResult = await structureGetContextStep({ env, contextKey });
|
|
@@ -36,7 +33,6 @@ export function createCompleteRowsTool({ datasetId, sandboxId, env }) {
|
|
|
36
33
|
const content = (ctxResult.data?.content ?? {});
|
|
37
34
|
const outputSchema = content?.structure?.outputSchema;
|
|
38
35
|
if (!outputSchema?.schema) {
|
|
39
|
-
console.log("[ekairos/structure] completeRows.tool execute schemaMissing");
|
|
40
36
|
return { success: false, error: "Schema not found in database. Please generate schema first." };
|
|
41
37
|
}
|
|
42
38
|
const schemaJson = outputSchema.schema;
|
|
@@ -48,11 +44,9 @@ export function createCompleteRowsTool({ datasetId, sandboxId, env }) {
|
|
|
48
44
|
catch (error) {
|
|
49
45
|
const message = error instanceof Error ? error.message : String(error);
|
|
50
46
|
if (mode === "auto") {
|
|
51
|
-
console.log("[ekairos/structure] completeRows.tool execute schemaCompileSkippedAuto");
|
|
52
47
|
validator = null;
|
|
53
48
|
}
|
|
54
49
|
else {
|
|
55
|
-
console.log("[ekairos/structure] completeRows.tool execute schemaCompileFailed");
|
|
56
50
|
return { success: false, error: `Failed to compile schema: ${message}` };
|
|
57
51
|
}
|
|
58
52
|
}
|
|
@@ -63,7 +57,6 @@ export function createCompleteRowsTool({ datasetId, sandboxId, env }) {
|
|
|
63
57
|
return validationResult;
|
|
64
58
|
totalValidRows = validationResult.validRowCount ?? 0;
|
|
65
59
|
}
|
|
66
|
-
console.log("[ekairos/structure] completeRows.tool execute validRowCount", totalValidRows);
|
|
67
60
|
const fileRead = await readDatasetSandboxFileStep({ env, sandboxId, path: outputPath });
|
|
68
61
|
if (!fileRead.contentBase64)
|
|
69
62
|
return { success: false, error: "Empty file content" };
|
|
@@ -74,8 +67,6 @@ export function createCompleteRowsTool({ datasetId, sandboxId, env }) {
|
|
|
74
67
|
});
|
|
75
68
|
if (!uploadResult.ok)
|
|
76
69
|
return { success: false, error: uploadResult.error };
|
|
77
|
-
console.log("[ekairos/structure] completeRows.tool execute uploadedFileId", uploadResult.data.fileId);
|
|
78
|
-
console.log("[ekairos/structure] completeRows.tool execute ok");
|
|
79
70
|
return {
|
|
80
71
|
success: true,
|
|
81
72
|
summary,
|
|
@@ -30,20 +30,15 @@ export function createExecuteCommandTool({ datasetId, sandboxId, env }) {
|
|
|
30
30
|
.describe("Name for the script file in snake_case (e.g., 'inspect_file', 'parse_csv', 'generate_output'). The file will be saved as <scriptName>.py in the workstation."),
|
|
31
31
|
}),
|
|
32
32
|
execute: async ({ pythonCode, scriptName }) => {
|
|
33
|
-
console.log("[ekairos/structure] executeCommand.tool execute begin");
|
|
34
|
-
console.log("[ekairos/structure] executeCommand.tool execute datasetId", datasetId);
|
|
35
|
-
console.log("[ekairos/structure] executeCommand.tool execute scriptName", scriptName);
|
|
36
33
|
const workstation = getDatasetWorkstation(datasetId);
|
|
37
34
|
const scriptNameWithExt = `${normalizeScriptName(scriptName)}.py`;
|
|
38
35
|
const scriptFile = `${workstation}/${scriptNameWithExt}`;
|
|
39
|
-
console.log("[ekairos/structure] executeCommand.tool execute writingScript");
|
|
40
36
|
await writeDatasetSandboxTextFileStep({
|
|
41
37
|
env,
|
|
42
38
|
sandboxId,
|
|
43
39
|
path: scriptFile,
|
|
44
40
|
text: pythonCode,
|
|
45
41
|
});
|
|
46
|
-
console.log("[ekairos/structure] executeCommand.tool execute runningPython");
|
|
47
42
|
const result = await runDatasetSandboxCommandStep({
|
|
48
43
|
env,
|
|
49
44
|
sandboxId,
|
|
@@ -58,7 +53,6 @@ export function createExecuteCommandTool({ datasetId, sandboxId, env }) {
|
|
|
58
53
|
const stdoutCapped = isStdoutTruncated ? stdout.slice(0, MAX_STDOUT_CHARS) : stdout;
|
|
59
54
|
const stderrCapped = isStderrTruncated ? stderr.slice(0, MAX_STDERR_CHARS) : stderr;
|
|
60
55
|
if (exitCode !== 0) {
|
|
61
|
-
console.log("[ekairos/structure] executeCommand.tool execute failedExitCode");
|
|
62
56
|
return {
|
|
63
57
|
success: false,
|
|
64
58
|
exitCode,
|
|
@@ -73,7 +67,6 @@ export function createExecuteCommandTool({ datasetId, sandboxId, env }) {
|
|
|
73
67
|
};
|
|
74
68
|
}
|
|
75
69
|
if (stderr && (stderr.includes("Traceback") || stderr.toLowerCase().includes("error"))) {
|
|
76
|
-
console.log("[ekairos/structure] executeCommand.tool execute pythonErrorDetected");
|
|
77
70
|
return {
|
|
78
71
|
success: false,
|
|
79
72
|
exitCode,
|
|
@@ -87,7 +80,6 @@ export function createExecuteCommandTool({ datasetId, sandboxId, env }) {
|
|
|
87
80
|
stderrOriginalLength: stderr.length,
|
|
88
81
|
};
|
|
89
82
|
}
|
|
90
|
-
console.log("[ekairos/structure] executeCommand.tool execute ok");
|
|
91
83
|
return {
|
|
92
84
|
success: true,
|
|
93
85
|
exitCode,
|
|
@@ -11,11 +11,6 @@ export function createGenerateSchemaTool({ datasetId, sandboxId, env }) {
|
|
|
11
11
|
schemaJson: z.string().describe("A JSON Schema string"),
|
|
12
12
|
}),
|
|
13
13
|
execute: async ({ schemaTitle, schemaDescription, schemaJson }) => {
|
|
14
|
-
console.log("[ekairos/structure] generateSchema.tool execute begin");
|
|
15
|
-
console.log("[ekairos/structure] generateSchema.tool execute datasetId", datasetId);
|
|
16
|
-
console.log("[ekairos/structure] generateSchema.tool execute sandboxId", sandboxId);
|
|
17
|
-
console.log("[ekairos/structure] generateSchema.tool execute schemaTitle", schemaTitle);
|
|
18
|
-
console.log("[ekairos/structure] generateSchema.tool execute schemaJsonLength", String(schemaJson ?? "").length);
|
|
19
14
|
const schemaData = {
|
|
20
15
|
title: schemaTitle,
|
|
21
16
|
description: schemaDescription,
|
|
@@ -24,17 +19,12 @@ export function createGenerateSchemaTool({ datasetId, sandboxId, env }) {
|
|
|
24
19
|
return JSON.parse(schemaJson);
|
|
25
20
|
}
|
|
26
21
|
catch (e) {
|
|
27
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
28
|
-
console.log("[ekairos/structure] generateSchema.tool execute invalidSchemaJson");
|
|
29
|
-
console.log("[ekairos/structure] generateSchema.tool execute invalidSchemaJsonMessage", message);
|
|
30
|
-
console.log("[ekairos/structure] generateSchema.tool execute invalidSchemaJsonPreview", String(schemaJson ?? "").slice(0, 300));
|
|
31
22
|
throw e;
|
|
32
23
|
}
|
|
33
24
|
})(),
|
|
34
25
|
};
|
|
35
26
|
const schemaPath = getDatasetOutputSchemaPath(datasetId);
|
|
36
27
|
try {
|
|
37
|
-
console.log("[ekairos/structure] generateSchema.tool execute writingSchemaPath", schemaPath);
|
|
38
28
|
await writeDatasetSandboxTextFileStep({
|
|
39
29
|
env,
|
|
40
30
|
sandboxId,
|
|
@@ -43,17 +33,9 @@ export function createGenerateSchemaTool({ datasetId, sandboxId, env }) {
|
|
|
43
33
|
});
|
|
44
34
|
}
|
|
45
35
|
catch (e) {
|
|
46
|
-
console.log("[ekairos/structure] generateSchema.tool execute failed");
|
|
47
36
|
const message = e instanceof Error ? e.message : String(e);
|
|
48
|
-
console.log("[ekairos/structure] generateSchema.tool execute failedMessage", message);
|
|
49
|
-
if (e instanceof Error && e.stack) {
|
|
50
|
-
console.log("[ekairos/structure] generateSchema.tool execute failedStack", e.stack);
|
|
51
|
-
}
|
|
52
|
-
console.log("[ekairos/structure] generateSchema.tool execute failedSchemaPath", schemaPath);
|
|
53
|
-
console.log("[ekairos/structure] generateSchema.tool execute failedHasEnv", Boolean(env));
|
|
54
37
|
return { success: false, error: message };
|
|
55
38
|
}
|
|
56
|
-
console.log("[ekairos/structure] generateSchema.tool execute ok");
|
|
57
39
|
return { success: true, message: "Schema written", schemaPath };
|
|
58
40
|
},
|
|
59
41
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StructureRowsOutputPagingCursor } from "./rowsOutputPaging";
|
|
2
|
+
export type StructureSplitRowsOutputToDatasetResult = {
|
|
3
|
+
datasetId?: string;
|
|
4
|
+
rowsWritten: number;
|
|
5
|
+
nextCursor: StructureRowsOutputPagingCursor;
|
|
6
|
+
done: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Step:
|
|
10
|
+
* Split a sandbox-local `output.jsonl` into a child dataset (also `output.jsonl`) of up to `limit` ROW entries.
|
|
11
|
+
*
|
|
12
|
+
* Key property:
|
|
13
|
+
* - Does NOT return rows; it persists a child dataset and returns only `{ datasetId, nextCursor, done }`.
|
|
14
|
+
*
|
|
15
|
+
* This is useful for workflows where you want to batch work (e.g. 300 rows) without moving large payloads
|
|
16
|
+
* through workflow/step params.
|
|
17
|
+
*/
|
|
18
|
+
export declare function structureSplitRowsOutputToDatasetStep(params: {
|
|
19
|
+
env: any;
|
|
20
|
+
sandboxId: string;
|
|
21
|
+
localPath: string;
|
|
22
|
+
cursor?: Partial<StructureRowsOutputPagingCursor>;
|
|
23
|
+
limit: number;
|
|
24
|
+
childDatasetId: string;
|
|
25
|
+
}): Promise<StructureSplitRowsOutputToDatasetResult>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getDatasetOutputPath, getDatasetWorkstation } from "./datasetFiles";
|
|
2
|
+
import { readDatasetSandboxFileStep, runDatasetSandboxCommandStep } from "./sandbox/steps";
|
|
3
|
+
import { getStoryRuntime } from "./runtime";
|
|
4
|
+
/**
|
|
5
|
+
* Step:
|
|
6
|
+
* Split a sandbox-local `output.jsonl` into a child dataset (also `output.jsonl`) of up to `limit` ROW entries.
|
|
7
|
+
*
|
|
8
|
+
* Key property:
|
|
9
|
+
* - Does NOT return rows; it persists a child dataset and returns only `{ datasetId, nextCursor, done }`.
|
|
10
|
+
*
|
|
11
|
+
* This is useful for workflows where you want to batch work (e.g. 300 rows) without moving large payloads
|
|
12
|
+
* through workflow/step params.
|
|
13
|
+
*/
|
|
14
|
+
export async function structureSplitRowsOutputToDatasetStep(params) {
|
|
15
|
+
"use step";
|
|
16
|
+
const byteOffset = params.cursor?.byteOffset ?? 0;
|
|
17
|
+
const rowOffset = params.cursor?.rowOffset ?? 0;
|
|
18
|
+
const workstation = getDatasetWorkstation(params.childDatasetId);
|
|
19
|
+
const outPath = getDatasetOutputPath(params.childDatasetId);
|
|
20
|
+
await runDatasetSandboxCommandStep({
|
|
21
|
+
env: params.env,
|
|
22
|
+
sandboxId: params.sandboxId,
|
|
23
|
+
cmd: "mkdir",
|
|
24
|
+
args: ["-p", workstation],
|
|
25
|
+
});
|
|
26
|
+
// Read from parent jsonl and write a child jsonl containing only ROW records, preserving `{ type, data }` lines.
|
|
27
|
+
const py = [
|
|
28
|
+
"import sys, json",
|
|
29
|
+
"in_path = sys.argv[1]",
|
|
30
|
+
"out_path = sys.argv[2]",
|
|
31
|
+
"byte_offset = int(sys.argv[3])",
|
|
32
|
+
"row_offset = int(sys.argv[4])",
|
|
33
|
+
"limit = int(sys.argv[5])",
|
|
34
|
+
"rows_written = 0",
|
|
35
|
+
"next_byte = byte_offset",
|
|
36
|
+
"next_row = row_offset",
|
|
37
|
+
"with open(in_path, 'rb') as f_in:",
|
|
38
|
+
" f_in.seek(byte_offset)",
|
|
39
|
+
" with open(out_path, 'wb') as f_out:",
|
|
40
|
+
" while rows_written < limit:",
|
|
41
|
+
" line = f_in.readline()",
|
|
42
|
+
" if not line:",
|
|
43
|
+
" break",
|
|
44
|
+
" next_byte = f_in.tell()",
|
|
45
|
+
" try:",
|
|
46
|
+
" obj = json.loads(line.decode('utf-8'))",
|
|
47
|
+
" except Exception:",
|
|
48
|
+
" continue",
|
|
49
|
+
" if obj.get('type') != 'row':",
|
|
50
|
+
" continue",
|
|
51
|
+
" f_out.write(line if line.endswith(b'\\n') else (line + b'\\n'))",
|
|
52
|
+
" rows_written += 1",
|
|
53
|
+
" next_row += 1",
|
|
54
|
+
"done = rows_written < limit",
|
|
55
|
+
"print(json.dumps({",
|
|
56
|
+
" 'rowsWritten': rows_written,",
|
|
57
|
+
" 'nextByteOffset': next_byte,",
|
|
58
|
+
" 'nextRowOffset': next_row,",
|
|
59
|
+
" 'done': done,",
|
|
60
|
+
"}))",
|
|
61
|
+
].join("\n");
|
|
62
|
+
const res = await runDatasetSandboxCommandStep({
|
|
63
|
+
env: params.env,
|
|
64
|
+
sandboxId: params.sandboxId,
|
|
65
|
+
cmd: "python",
|
|
66
|
+
args: ["-c", py, params.localPath, outPath, String(byteOffset), String(rowOffset), String(params.limit)],
|
|
67
|
+
});
|
|
68
|
+
if (res.exitCode !== 0) {
|
|
69
|
+
throw new Error(res.stderr || "Failed to split rows output to dataset");
|
|
70
|
+
}
|
|
71
|
+
const parsed = JSON.parse(String(res.stdout ?? "").trim());
|
|
72
|
+
const rowsWritten = Number(parsed?.rowsWritten ?? 0);
|
|
73
|
+
const nextCursor = {
|
|
74
|
+
byteOffset: Number(parsed?.nextByteOffset ?? byteOffset),
|
|
75
|
+
rowOffset: Number(parsed?.nextRowOffset ?? rowOffset),
|
|
76
|
+
};
|
|
77
|
+
const done = Boolean(parsed?.done);
|
|
78
|
+
// No work to persist: return only paging state.
|
|
79
|
+
if (rowsWritten <= 0) {
|
|
80
|
+
return { datasetId: undefined, rowsWritten: 0, nextCursor, done: true };
|
|
81
|
+
}
|
|
82
|
+
const fileRes = await readDatasetSandboxFileStep({
|
|
83
|
+
env: params.env,
|
|
84
|
+
sandboxId: params.sandboxId,
|
|
85
|
+
path: outPath,
|
|
86
|
+
});
|
|
87
|
+
const storyRuntime = await getStoryRuntime(params.env);
|
|
88
|
+
const db = storyRuntime.db;
|
|
89
|
+
const store = storyRuntime.store;
|
|
90
|
+
const storagePath = `/structure/${params.childDatasetId}/output.jsonl`;
|
|
91
|
+
const fileBuffer = Buffer.from(fileRes.contentBase64 ?? "", "base64");
|
|
92
|
+
const uploadResult = await db.storage.uploadFile(storagePath, fileBuffer, {
|
|
93
|
+
contentType: "application/x-ndjson",
|
|
94
|
+
contentDisposition: "output.jsonl",
|
|
95
|
+
});
|
|
96
|
+
const fileId = uploadResult?.data?.id;
|
|
97
|
+
if (!fileId)
|
|
98
|
+
throw new Error("Failed to upload child dataset output file to storage");
|
|
99
|
+
const contextKey = `structure:${params.childDatasetId}`;
|
|
100
|
+
const ctx = await store.getOrCreateContext({ key: contextKey });
|
|
101
|
+
const ctxId = ctx?.id;
|
|
102
|
+
if (!ctxId)
|
|
103
|
+
throw new Error("Failed to create child dataset context");
|
|
104
|
+
// Link the output file to the context (used by DatasetService.readRecordsFromFile).
|
|
105
|
+
await db.transact([db.tx.context_contexts[ctxId].link({ structure_output_file: fileId })]);
|
|
106
|
+
// Patch metadata under `structure` namespace (never clobber Story runtime keys).
|
|
107
|
+
const existingContent = (ctx?.content ?? {});
|
|
108
|
+
const existingStructure = (existingContent?.structure ?? {});
|
|
109
|
+
const updatedAt = Date.now();
|
|
110
|
+
await store.updateContextContent({ key: contextKey }, {
|
|
111
|
+
...existingContent,
|
|
112
|
+
structure: {
|
|
113
|
+
...existingStructure,
|
|
114
|
+
kind: "ekairos.structure",
|
|
115
|
+
version: Number(existingStructure?.version ?? 1),
|
|
116
|
+
structureId: params.childDatasetId,
|
|
117
|
+
output: "rows",
|
|
118
|
+
updatedAt,
|
|
119
|
+
outputs: {
|
|
120
|
+
...(existingStructure?.outputs ?? {}),
|
|
121
|
+
rows: {
|
|
122
|
+
format: "jsonl",
|
|
123
|
+
fileId,
|
|
124
|
+
storagePath,
|
|
125
|
+
rowCount: rowsWritten,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
return { datasetId: params.childDatasetId, rowsWritten, nextCursor, done };
|
|
131
|
+
}
|
package/dist/sandbox/steps.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export async function createDatasetSandboxStep(params) {
|
|
2
2
|
"use step";
|
|
3
|
-
console.log("[ekairos/structure] sandbox.step createSandbox begin");
|
|
4
|
-
console.log("[ekairos/structure] sandbox.step createSandbox runtime", params.runtime);
|
|
5
3
|
const { resolveStoryRuntime } = await import("@ekairos/story/runtime");
|
|
6
4
|
const db = (await resolveStoryRuntime(params.env)).db;
|
|
7
5
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
@@ -9,15 +7,10 @@ export async function createDatasetSandboxStep(params) {
|
|
|
9
7
|
const created = await service.createSandbox(params);
|
|
10
8
|
if (!created.ok)
|
|
11
9
|
throw new Error(created.error);
|
|
12
|
-
console.log("[ekairos/structure] sandbox.step createSandbox ok");
|
|
13
|
-
console.log("[ekairos/structure] sandbox.step createSandbox sandboxId", created.data.sandboxId);
|
|
14
10
|
return { sandboxId: created.data.sandboxId };
|
|
15
11
|
}
|
|
16
12
|
export async function runDatasetSandboxCommandStep(params) {
|
|
17
13
|
"use step";
|
|
18
|
-
console.log("[ekairos/structure] sandbox.step runCommand begin");
|
|
19
|
-
console.log("[ekairos/structure] sandbox.step runCommand sandboxId", params.sandboxId);
|
|
20
|
-
console.log("[ekairos/structure] sandbox.step runCommand cmd", params.cmd);
|
|
21
14
|
const { resolveStoryRuntime } = await import("@ekairos/story/runtime");
|
|
22
15
|
const db = (await resolveStoryRuntime(params.env)).db;
|
|
23
16
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
@@ -30,15 +23,10 @@ export async function runDatasetSandboxCommandStep(params) {
|
|
|
30
23
|
stdout: result.data.output ?? "",
|
|
31
24
|
stderr: result.data.error ?? "",
|
|
32
25
|
};
|
|
33
|
-
console.log("[ekairos/structure] sandbox.step runCommand ok");
|
|
34
|
-
console.log("[ekairos/structure] sandbox.step runCommand exitCode", normalized.exitCode);
|
|
35
26
|
return normalized;
|
|
36
27
|
}
|
|
37
28
|
export async function writeDatasetSandboxFilesStep(params) {
|
|
38
29
|
"use step";
|
|
39
|
-
console.log("[ekairos/structure] sandbox.step writeFiles begin");
|
|
40
|
-
console.log("[ekairos/structure] sandbox.step writeFiles sandboxId", params.sandboxId);
|
|
41
|
-
console.log("[ekairos/structure] sandbox.step writeFiles paths", params.files.map((f) => f.path));
|
|
42
30
|
const { resolveStoryRuntime } = await import("@ekairos/story/runtime");
|
|
43
31
|
const db = (await resolveStoryRuntime(params.env)).db;
|
|
44
32
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
@@ -46,7 +34,6 @@ export async function writeDatasetSandboxFilesStep(params) {
|
|
|
46
34
|
const result = await service.writeFiles(params.sandboxId, params.files);
|
|
47
35
|
if (!result.ok)
|
|
48
36
|
throw new Error(result.error);
|
|
49
|
-
console.log("[ekairos/structure] sandbox.step writeFiles ok");
|
|
50
37
|
}
|
|
51
38
|
/**
|
|
52
39
|
* Workflow-safe helper:
|
|
@@ -66,9 +53,6 @@ export async function writeDatasetSandboxTextFileStep(params) {
|
|
|
66
53
|
}
|
|
67
54
|
export async function readDatasetSandboxFileStep(params) {
|
|
68
55
|
"use step";
|
|
69
|
-
console.log("[ekairos/structure] sandbox.step readFile begin");
|
|
70
|
-
console.log("[ekairos/structure] sandbox.step readFile sandboxId", params.sandboxId);
|
|
71
|
-
console.log("[ekairos/structure] sandbox.step readFile path", params.path);
|
|
72
56
|
const { resolveStoryRuntime } = await import("@ekairos/story/runtime");
|
|
73
57
|
const db = (await resolveStoryRuntime(params.env)).db;
|
|
74
58
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
@@ -76,7 +60,6 @@ export async function readDatasetSandboxFileStep(params) {
|
|
|
76
60
|
const result = await service.readFile(params.sandboxId, params.path);
|
|
77
61
|
if (!result.ok)
|
|
78
62
|
throw new Error(result.error);
|
|
79
|
-
console.log("[ekairos/structure] sandbox.step readFile ok");
|
|
80
63
|
return result.data;
|
|
81
64
|
}
|
|
82
65
|
/**
|
|
@@ -94,8 +77,6 @@ export async function readDatasetSandboxTextFileStep(params) {
|
|
|
94
77
|
}
|
|
95
78
|
export async function stopDatasetSandboxStep(params) {
|
|
96
79
|
"use step";
|
|
97
|
-
console.log("[ekairos/structure] sandbox.step stopSandbox begin");
|
|
98
|
-
console.log("[ekairos/structure] sandbox.step stopSandbox sandboxId", params.sandboxId);
|
|
99
80
|
const { resolveStoryRuntime } = await import("@ekairos/story/runtime");
|
|
100
81
|
const db = (await resolveStoryRuntime(params.env)).db;
|
|
101
82
|
const { SandboxService } = (await import("@ekairos/sandbox"));
|
|
@@ -103,5 +84,4 @@ export async function stopDatasetSandboxStep(params) {
|
|
|
103
84
|
const result = await service.stopSandbox(params.sandboxId);
|
|
104
85
|
if (!result.ok)
|
|
105
86
|
throw new Error(result.error);
|
|
106
|
-
console.log("[ekairos/structure] sandbox.step stopSandbox ok");
|
|
107
87
|
}
|
|
@@ -39,11 +39,8 @@ export async function persistObjectResultFromStoryStep(params) {
|
|
|
39
39
|
const { resolveStoryRuntime } = await import("@ekairos/story/runtime");
|
|
40
40
|
const runtime = (await resolveStoryRuntime(params.env));
|
|
41
41
|
const store = runtime.store;
|
|
42
|
-
console.log("[ekairos/structure] persistObjectFromStory.step execute begin");
|
|
43
|
-
console.log("[ekairos/structure] persistObjectFromStory.step execute datasetId", params.datasetId);
|
|
44
42
|
const contextKey = `structure:${params.datasetId}`;
|
|
45
43
|
const events = await store.getEvents({ key: contextKey });
|
|
46
|
-
console.log("[ekairos/structure] persistObjectFromStory.step execute eventsCount", Array.isArray(events) ? events.length : -1);
|
|
47
44
|
const { structurePatchContextContentStep, structureGetContextStep } = await import("../dataset/steps");
|
|
48
45
|
const ctxResult = await structureGetContextStep({ env: params.env, contextKey });
|
|
49
46
|
const existingContent = ctxResult.ok ? (ctxResult.data?.content ?? {}) : {};
|
|
@@ -58,7 +55,6 @@ export async function persistObjectResultFromStoryStep(params) {
|
|
|
58
55
|
.trim();
|
|
59
56
|
const obj = extractJsonObject(text);
|
|
60
57
|
if (obj) {
|
|
61
|
-
console.log("[ekairos/structure] persistObjectFromStory.step execute parsedJsonOk");
|
|
62
58
|
const patchResult = await structurePatchContextContentStep({
|
|
63
59
|
env: params.env,
|
|
64
60
|
contextKey,
|
|
@@ -80,10 +76,8 @@ export async function persistObjectResultFromStoryStep(params) {
|
|
|
80
76
|
const err = patchResult?.error ?? "Failed to persist object result";
|
|
81
77
|
throw new Error(err);
|
|
82
78
|
}
|
|
83
|
-
console.log("[ekairos/structure] persistObjectFromStory.step execute savedOk");
|
|
84
79
|
return { ok: true };
|
|
85
80
|
}
|
|
86
81
|
}
|
|
87
|
-
console.log("[ekairos/structure] persistObjectFromStory.step execute noJsonFound");
|
|
88
82
|
return { ok: false };
|
|
89
83
|
}
|
package/dist/structure.d.ts
CHANGED
|
@@ -18,6 +18,16 @@ export type StructureRowsReadResult = {
|
|
|
18
18
|
cursor: StructureRowsOutputPagingCursor;
|
|
19
19
|
done: boolean;
|
|
20
20
|
};
|
|
21
|
+
export type StructureRowsSplitResult = {
|
|
22
|
+
/**
|
|
23
|
+
* Child datasetId containing a JSONL `output.jsonl` with up to `limit` ROW entries.
|
|
24
|
+
* Omitted when there are no more rows to split.
|
|
25
|
+
*/
|
|
26
|
+
datasetId?: string;
|
|
27
|
+
rowsWritten: number;
|
|
28
|
+
cursor: StructureRowsOutputPagingCursor;
|
|
29
|
+
done: boolean;
|
|
30
|
+
};
|
|
21
31
|
export type StructureRowsReader = {
|
|
22
32
|
/**
|
|
23
33
|
* Workflow-friendly rows reader.
|
|
@@ -33,6 +43,18 @@ export type StructureRowsReader = {
|
|
|
33
43
|
cursor?: Partial<StructureRowsOutputPagingCursor>;
|
|
34
44
|
limit?: number;
|
|
35
45
|
}): Promise<StructureRowsReadResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Split the rows output into a child dataset (jsonl) and return paging state.
|
|
48
|
+
*
|
|
49
|
+
* Unlike `read()`, this does not return `rows[]` (avoids moving payloads through params/results).
|
|
50
|
+
*/
|
|
51
|
+
split(): Promise<StructureRowsSplitResult>;
|
|
52
|
+
split(cursor?: Partial<StructureRowsOutputPagingCursor>, limit?: number): Promise<StructureRowsSplitResult>;
|
|
53
|
+
split(params?: {
|
|
54
|
+
cursor?: Partial<StructureRowsOutputPagingCursor>;
|
|
55
|
+
limit?: number;
|
|
56
|
+
datasetId?: string;
|
|
57
|
+
}): Promise<StructureRowsSplitResult>;
|
|
36
58
|
};
|
|
37
59
|
export type StructureBuildResult = {
|
|
38
60
|
datasetId: string;
|
package/dist/structure.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createStory, didToolExecute, USER_MESSAGE_TYPE, WEB_CHANNEL } from "@ekairos/story";
|
|
2
2
|
import { getDatasetOutputPath, getDatasetOutputSchemaPath, getDatasetWorkstation } from "./datasetFiles";
|
|
3
3
|
import { structureDownloadRowsOutputToSandboxStep, structureReadRowsOutputPageFromSandboxStep, } from "./rowsOutputPaging";
|
|
4
|
+
import { structureSplitRowsOutputToDatasetStep } from "./rowsOutputSplit";
|
|
4
5
|
import { createDatasetSandboxStep, readDatasetSandboxFileStep, readDatasetSandboxTextFileStep, runDatasetSandboxCommandStep, writeDatasetSandboxFilesStep, writeDatasetSandboxTextFileStep, } from "./sandbox/steps";
|
|
5
6
|
import { readInstantFileStep } from "./file/steps";
|
|
6
7
|
import { structureGetContextStep, structureGetContextWithRowsOutputFileStep, structureReadRowsOutputJsonlStep, } from "./dataset/steps";
|
|
8
|
+
import { getWorkflowMetadata } from "workflow";
|
|
7
9
|
import { buildStructurePrompt } from "./prompts";
|
|
8
10
|
import { createExecuteCommandTool } from "./executeCommand.tool";
|
|
9
11
|
import { createGenerateSchemaTool } from "./generateSchema.tool";
|
|
@@ -20,6 +22,21 @@ function createUuidV4() {
|
|
|
20
22
|
return v.toString(16);
|
|
21
23
|
});
|
|
22
24
|
}
|
|
25
|
+
function assertRunningInsideWorkflow(params) {
|
|
26
|
+
try {
|
|
27
|
+
const meta = getWorkflowMetadata();
|
|
28
|
+
const runId = meta?.workflowRunId;
|
|
29
|
+
if (!runId) {
|
|
30
|
+
throw new Error("Missing workflowRunId");
|
|
31
|
+
}
|
|
32
|
+
return meta;
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
36
|
+
throw new Error(`@ekairos/structure: structure().build() must be called from a "use workflow" function. ` +
|
|
37
|
+
`datasetId=${params.datasetId}. getWorkflowMetadata failed: ${msg}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
23
40
|
function guessTextFileExtension(mimeType, name) {
|
|
24
41
|
const n = String(name ?? "").toLowerCase();
|
|
25
42
|
if (n.includes("."))
|
|
@@ -38,18 +55,11 @@ async function ensureSandboxPrepared(params) {
|
|
|
38
55
|
const workstation = getDatasetWorkstation(datasetId);
|
|
39
56
|
const outputPath = getDatasetOutputPath(datasetId);
|
|
40
57
|
if (state.initialized) {
|
|
41
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared alreadyInitialized");
|
|
42
58
|
return { preparedSources: state.sources, workstation, outputPath };
|
|
43
59
|
}
|
|
44
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared begin");
|
|
45
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared datasetId", datasetId);
|
|
46
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared sourcesCount", sources.length);
|
|
47
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared sandbox.mkdir", workstation);
|
|
48
60
|
const mkdirRes = await runDatasetSandboxCommandStep({ env, sandboxId, cmd: "mkdir", args: ["-p", workstation] });
|
|
49
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared sandbox.mkdir exitCode", mkdirRes.exitCode);
|
|
50
61
|
// Align with dataset sandbox behavior: install python deps up-front (once per dataset sandbox).
|
|
51
62
|
// This avoids tool-level "install if used" heuristics and ensures scripts can import pandas.
|
|
52
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared installingPythonDeps pandas+openpyxl");
|
|
53
63
|
const pipInstall = await runDatasetSandboxCommandStep({
|
|
54
64
|
env,
|
|
55
65
|
sandboxId,
|
|
@@ -65,8 +75,6 @@ async function ensureSandboxPrepared(params) {
|
|
|
65
75
|
for (let i = 0; i < sources.length; i++) {
|
|
66
76
|
const src = sources[i];
|
|
67
77
|
if (src.kind === "file") {
|
|
68
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared preparingFileSource");
|
|
69
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared fileId", src.fileId);
|
|
70
78
|
const file = await readInstantFileStep({ env, fileId: src.fileId });
|
|
71
79
|
const fileName = String(file.contentDisposition ?? "");
|
|
72
80
|
const ext = fileName.includes(".") ? fileName.substring(fileName.lastIndexOf(".")) : "";
|
|
@@ -76,13 +84,10 @@ async function ensureSandboxPrepared(params) {
|
|
|
76
84
|
sandboxId,
|
|
77
85
|
files: [{ path, contentBase64: file.contentBase64 }],
|
|
78
86
|
});
|
|
79
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared wroteFile path", path);
|
|
80
87
|
prepared.push({ kind: "file", id: src.fileId, path });
|
|
81
88
|
continue;
|
|
82
89
|
}
|
|
83
90
|
if (src.kind === "dataset") {
|
|
84
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared preparingDatasetSource");
|
|
85
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared sourceDatasetId", src.datasetId);
|
|
86
91
|
const content = await structureReadRowsOutputJsonlStep({ env, structureId: src.datasetId });
|
|
87
92
|
if (!content.ok) {
|
|
88
93
|
throw new Error(content.error);
|
|
@@ -93,38 +98,30 @@ async function ensureSandboxPrepared(params) {
|
|
|
93
98
|
sandboxId,
|
|
94
99
|
files: [{ path, contentBase64: content.data.contentBase64 }],
|
|
95
100
|
});
|
|
96
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared wroteDatasetJsonl path", path);
|
|
97
101
|
prepared.push({ kind: "dataset", id: src.datasetId, path });
|
|
98
102
|
continue;
|
|
99
103
|
}
|
|
100
104
|
if (src.kind === "text") {
|
|
101
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared preparingTextSource");
|
|
102
105
|
const ext = guessTextFileExtension(src.mimeType, src.name);
|
|
103
106
|
const textId = `text_${i}`;
|
|
104
107
|
const path = `${workstation}/${textId}${ext}`;
|
|
105
108
|
await writeDatasetSandboxTextFileStep({ env, sandboxId, path, text: String(src.text ?? "") });
|
|
106
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared wroteText path", path);
|
|
107
109
|
prepared.push({ kind: "text", id: textId, path, name: src.name, mimeType: src.mimeType });
|
|
108
110
|
continue;
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
state.initialized = true;
|
|
112
114
|
state.sources = prepared;
|
|
113
|
-
console.log("[ekairos/structure] structure.ts ensureSandboxPrepared ok");
|
|
114
115
|
return { preparedSources: prepared, workstation, outputPath };
|
|
115
116
|
}
|
|
116
117
|
async function readSchemaFromSandboxIfPresent(params) {
|
|
117
118
|
const schemaPath = getDatasetOutputSchemaPath(params.datasetId);
|
|
118
|
-
console.log("[ekairos/structure] structure.ts readSchemaFromSandboxIfPresent begin");
|
|
119
|
-
console.log("[ekairos/structure] structure.ts readSchemaFromSandboxIfPresent datasetId", params.datasetId);
|
|
120
|
-
console.log("[ekairos/structure] structure.ts readSchemaFromSandboxIfPresent schemaPath", schemaPath);
|
|
121
119
|
const exists = await runDatasetSandboxCommandStep({
|
|
122
120
|
env: params.env,
|
|
123
121
|
sandboxId: params.sandboxId,
|
|
124
122
|
cmd: "test",
|
|
125
123
|
args: ["-f", schemaPath],
|
|
126
124
|
});
|
|
127
|
-
console.log("[ekairos/structure] structure.ts readSchemaFromSandboxIfPresent existsExitCode", exists.exitCode);
|
|
128
125
|
if (exists.exitCode !== 0)
|
|
129
126
|
return null;
|
|
130
127
|
const fileRead = await readDatasetSandboxFileStep({
|
|
@@ -143,11 +140,9 @@ async function readSchemaFromSandboxIfPresent(params) {
|
|
|
143
140
|
return null;
|
|
144
141
|
try {
|
|
145
142
|
const parsed = JSON.parse(jsonText);
|
|
146
|
-
console.log("[ekairos/structure] structure.ts readSchemaFromSandboxIfPresent parsedOk");
|
|
147
143
|
return parsed;
|
|
148
144
|
}
|
|
149
145
|
catch {
|
|
150
|
-
console.log("[ekairos/structure] structure.ts readSchemaFromSandboxIfPresent parsedFailed");
|
|
151
146
|
return null;
|
|
152
147
|
}
|
|
153
148
|
}
|
|
@@ -156,13 +151,11 @@ function createStructureStoryDefinition(config) {
|
|
|
156
151
|
const model = config.model ?? "openai/gpt-5.2";
|
|
157
152
|
const story = createStory("ekairos.structure")
|
|
158
153
|
.context(async (stored, env) => {
|
|
159
|
-
console.log("[ekairos/structure] structure.ts story.context begin");
|
|
160
154
|
const prev = stored?.content ?? {};
|
|
161
155
|
const sandboxState = prev.sandboxState ?? { initialized: false, sources: [] };
|
|
162
156
|
const existingSandboxId = prev.sandboxId ?? config.sandboxId ?? "";
|
|
163
157
|
let sandboxId = existingSandboxId;
|
|
164
158
|
if (!sandboxId) {
|
|
165
|
-
console.log("[ekairos/structure] structure.ts story.context creatingSandbox");
|
|
166
159
|
const created = await createDatasetSandboxStep({ env, runtime: "python3.13", timeoutMs: 10 * 60 * 1000 });
|
|
167
160
|
sandboxId = created.sandboxId;
|
|
168
161
|
}
|
|
@@ -174,7 +167,6 @@ function createStructureStoryDefinition(config) {
|
|
|
174
167
|
state: sandboxState,
|
|
175
168
|
});
|
|
176
169
|
if (config.mode === "schema" && config.outputSchema) {
|
|
177
|
-
console.log("[ekairos/structure] structure.ts story.context schemaMode outputSchemaProvided");
|
|
178
170
|
}
|
|
179
171
|
const promptContext = {
|
|
180
172
|
datasetId,
|
|
@@ -231,15 +223,12 @@ function createStructureStoryDefinition(config) {
|
|
|
231
223
|
};
|
|
232
224
|
})
|
|
233
225
|
.narrative(async (stored) => {
|
|
234
|
-
console.log("[ekairos/structure] structure.ts story.narrative begin");
|
|
235
226
|
const promptContext = stored?.content?.promptContext;
|
|
236
227
|
const base = buildStructurePrompt(promptContext);
|
|
237
228
|
const userInstructions = String(config.instructions ?? "").trim();
|
|
238
229
|
if (!userInstructions) {
|
|
239
|
-
console.log("[ekairos/structure] structure.ts story.narrative okNoUserInstructions");
|
|
240
230
|
return base;
|
|
241
231
|
}
|
|
242
|
-
console.log("[ekairos/structure] structure.ts story.narrative okWithUserInstructions");
|
|
243
232
|
return [
|
|
244
233
|
"## USER INSTRUCTIONS",
|
|
245
234
|
"The following instructions were provided by the user. Apply them in addition to (and with higher priority than) the default instructions.",
|
|
@@ -250,14 +239,10 @@ function createStructureStoryDefinition(config) {
|
|
|
250
239
|
].join("\n");
|
|
251
240
|
})
|
|
252
241
|
.actions(async (stored, env) => {
|
|
253
|
-
console.log("[ekairos/structure] structure.ts story.actions begin");
|
|
254
242
|
const sandboxId = stored?.content?.sandboxId;
|
|
255
243
|
const output = config.output;
|
|
256
244
|
const content = (stored?.content ?? {});
|
|
257
245
|
const hasOutputSchema = Boolean(content?.structure?.outputSchema?.schema);
|
|
258
|
-
console.log("[ekairos/structure] structure.ts story.actions datasetId", datasetId);
|
|
259
|
-
console.log("[ekairos/structure] structure.ts story.actions mode", config.mode);
|
|
260
|
-
console.log("[ekairos/structure] structure.ts story.actions hasOutputSchema", hasOutputSchema);
|
|
261
246
|
const actions = {
|
|
262
247
|
executeCommand: createExecuteCommandTool({ datasetId, sandboxId, env }),
|
|
263
248
|
clear: createClearDatasetTool({ datasetId, sandboxId, env }),
|
|
@@ -274,7 +259,6 @@ function createStructureStoryDefinition(config) {
|
|
|
274
259
|
? createCompleteRowsTool({ datasetId, sandboxId, env })
|
|
275
260
|
: createCompleteObjectTool({ datasetId, sandboxId, env });
|
|
276
261
|
}
|
|
277
|
-
console.log("[ekairos/structure] structure.ts story.actions ok");
|
|
278
262
|
return actions;
|
|
279
263
|
})
|
|
280
264
|
.shouldContinue(({ reactionEvent }) => {
|
|
@@ -319,11 +303,9 @@ export function structure(env, opts) {
|
|
|
319
303
|
return api;
|
|
320
304
|
},
|
|
321
305
|
async build(userPrompt) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
console.log("[ekairos/structure] structure.ts build output", output);
|
|
326
|
-
console.log("[ekairos/structure] structure.ts build sourcesCount", sources.length);
|
|
306
|
+
// Guardrail: structure build MUST run inside workflow runtime ("use workflow").
|
|
307
|
+
const workflowMeta = assertRunningInsideWorkflow({ datasetId });
|
|
308
|
+
void workflowMeta?.workflowRunId;
|
|
327
309
|
const contextKey = `structure:${datasetId}`;
|
|
328
310
|
const storyConfig = {
|
|
329
311
|
datasetId,
|
|
@@ -344,22 +326,14 @@ export function structure(env, opts) {
|
|
|
344
326
|
};
|
|
345
327
|
}
|
|
346
328
|
async function runStory(evt) {
|
|
347
|
-
console.log("[ekairos/structure] structure.ts build storyReact begin");
|
|
348
|
-
console.log("[ekairos/structure] structure.ts build storyReact contextKey", contextKey);
|
|
349
|
-
console.log("[ekairos/structure] structure.ts build storyReact silent", true);
|
|
350
|
-
console.log("[ekairos/structure] structure.ts build storyReact eventId", evt?.id);
|
|
351
|
-
console.log("[ekairos/structure] structure.ts build storyReact eventType", evt?.type);
|
|
352
329
|
await story.react(evt, {
|
|
353
330
|
env,
|
|
354
331
|
context: { key: contextKey },
|
|
355
332
|
options: { silent: true, preventClose: true, sendFinish: false, maxIterations: 40, maxModelSteps: 10 },
|
|
356
333
|
});
|
|
357
|
-
console.log("[ekairos/structure] structure.ts build storyReact end");
|
|
358
334
|
// Tools are intentionally pure: persist completion outputs post-react by reading tool results from events.
|
|
359
335
|
const commit = await structureCommitFromEventsStep({ env, structureId: datasetId });
|
|
360
336
|
if (!commit.ok) {
|
|
361
|
-
console.log("[ekairos/structure] structure.ts build commitFromEvents failed");
|
|
362
|
-
console.log("[ekairos/structure] structure.ts build commitFromEvents error", commit.error);
|
|
363
337
|
}
|
|
364
338
|
}
|
|
365
339
|
async function getContextOrThrow() {
|
|
@@ -388,7 +362,6 @@ export function structure(env, opts) {
|
|
|
388
362
|
const content = (ctx?.content ?? {});
|
|
389
363
|
const hasSchema = Boolean(content?.structure?.outputSchema?.schema);
|
|
390
364
|
if (!hasSchema) {
|
|
391
|
-
console.log("[ekairos/structure] structure.ts build autoSchemaMissing followUp");
|
|
392
365
|
await runStory(makeUserMessageEvent([
|
|
393
366
|
"CRITICAL: You did not generate a schema yet.",
|
|
394
367
|
"1) Investigate Sources using executeCommand (inspect paths, read files, infer structure).",
|
|
@@ -399,38 +372,30 @@ export function structure(env, opts) {
|
|
|
399
372
|
}
|
|
400
373
|
}
|
|
401
374
|
const needsSecondPass = output === "rows" ? !(await isRowsCompleted()) : !isObjectCompleted(ctx);
|
|
402
|
-
console.log("[ekairos/structure] structure.ts build needsSecondPass", needsSecondPass);
|
|
403
375
|
if (needsSecondPass) {
|
|
404
|
-
console.log("[ekairos/structure] structure.ts build secondPass begin");
|
|
405
376
|
const followUpText = output === "rows"
|
|
406
377
|
? "Finalize now: write output.jsonl to OutputPath and call complete."
|
|
407
378
|
: "Finalize now: call complete with summary and resultJson (inline JSON).";
|
|
408
379
|
await runStory(makeUserMessageEvent(followUpText));
|
|
409
380
|
ctx = await getContextOrThrow();
|
|
410
|
-
console.log("[ekairos/structure] structure.ts build secondPass end");
|
|
411
381
|
}
|
|
412
382
|
const stillIncompleteAfterSecondPass = output === "rows" ? !(await isRowsCompleted()) : !isObjectCompleted(ctx);
|
|
413
383
|
if (stillIncompleteAfterSecondPass) {
|
|
414
|
-
console.log("[ekairos/structure] structure.ts build thirdPass begin");
|
|
415
384
|
const finalText = output === "rows"
|
|
416
385
|
? "CRITICAL: Do not do anything else. Ensure output.jsonl exists at OutputPath and immediately call complete."
|
|
417
386
|
: "CRITICAL: Do not do anything else. Immediately call complete with summary and resultJson (inline JSON).";
|
|
418
387
|
await runStory(makeUserMessageEvent(finalText));
|
|
419
388
|
ctx = await getContextOrThrow();
|
|
420
|
-
console.log("[ekairos/structure] structure.ts build thirdPass end");
|
|
421
389
|
}
|
|
422
390
|
if (output === "rows" && !(await isRowsCompleted())) {
|
|
423
391
|
throw new Error("Rows output not completed");
|
|
424
392
|
}
|
|
425
393
|
if (output === "object" && !isObjectCompleted(ctx)) {
|
|
426
|
-
console.log("[ekairos/structure] structure.ts build objectNotCompleted tryingPersistFallback");
|
|
427
394
|
const persisted = await persistObjectResultFromStoryStep({ env, datasetId });
|
|
428
395
|
if (persisted.ok) {
|
|
429
|
-
console.log("[ekairos/structure] structure.ts build persistFallback ok");
|
|
430
396
|
ctx = await getContextOrThrow();
|
|
431
397
|
}
|
|
432
398
|
else {
|
|
433
|
-
console.log("[ekairos/structure] structure.ts build persistFallback failed");
|
|
434
399
|
}
|
|
435
400
|
}
|
|
436
401
|
if (output === "object" && !isObjectCompleted(ctx)) {
|
|
@@ -464,8 +429,40 @@ export function structure(env, opts) {
|
|
|
464
429
|
done: page.done,
|
|
465
430
|
};
|
|
466
431
|
},
|
|
432
|
+
split: async (cursorOrParams, limit) => {
|
|
433
|
+
if (output !== "rows") {
|
|
434
|
+
throw new Error("reader.split() is only supported for output=rows");
|
|
435
|
+
}
|
|
436
|
+
if (!rowsSandboxRef) {
|
|
437
|
+
rowsSandboxRef = await structureDownloadRowsOutputToSandboxStep({
|
|
438
|
+
env,
|
|
439
|
+
structureId: datasetId,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const params = cursorOrParams && typeof cursorOrParams === "object" && ("cursor" in cursorOrParams || "limit" in cursorOrParams)
|
|
443
|
+
? cursorOrParams
|
|
444
|
+
: {
|
|
445
|
+
cursor: cursorOrParams,
|
|
446
|
+
limit,
|
|
447
|
+
datasetId: undefined,
|
|
448
|
+
};
|
|
449
|
+
const childDatasetId = params?.datasetId ?? createUuidV4();
|
|
450
|
+
const res = await structureSplitRowsOutputToDatasetStep({
|
|
451
|
+
env,
|
|
452
|
+
sandboxId: rowsSandboxRef.sandboxId,
|
|
453
|
+
localPath: rowsSandboxRef.localPath,
|
|
454
|
+
cursor: params?.cursor,
|
|
455
|
+
limit: params?.limit ?? 300,
|
|
456
|
+
childDatasetId,
|
|
457
|
+
});
|
|
458
|
+
return {
|
|
459
|
+
datasetId: res.datasetId,
|
|
460
|
+
rowsWritten: res.rowsWritten,
|
|
461
|
+
cursor: res.nextCursor,
|
|
462
|
+
done: res.done,
|
|
463
|
+
};
|
|
464
|
+
},
|
|
467
465
|
};
|
|
468
|
-
console.log("[ekairos/structure] structure.ts build ok");
|
|
469
466
|
return output === "object" ? { datasetId, reader, dataset: ctx } : { datasetId, reader };
|
|
470
467
|
},
|
|
471
468
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekairos/structure",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.69-beta.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,12 +36,13 @@
|
|
|
36
36
|
"typecheck": "tsc --noEmit"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ekairos/domain": "^1.21.
|
|
39
|
+
"@ekairos/domain": "^1.21.69-beta.0",
|
|
40
40
|
"@ekairos/sandbox": "^1.21.60-beta.0",
|
|
41
41
|
"@instantdb/admin": "^0.22.13",
|
|
42
42
|
"@instantdb/core": "^0.22.13",
|
|
43
43
|
"ai": "^5.0.95",
|
|
44
44
|
"ajv": "^8.17.1",
|
|
45
|
+
"workflow": "4.0.1-beta.41",
|
|
45
46
|
"zod": "^4.1.8"
|
|
46
47
|
},
|
|
47
48
|
"peerDependencies": {
|