deepline 0.1.32 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -20
- package/dist/cli/index.js +1117 -381
- package/dist/cli/index.mjs +1058 -322
- package/dist/index.d.mts +131 -85
- package/dist/index.d.ts +131 -85
- package/dist/index.js +569 -124
- package/dist/index.mjs +570 -125
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +212 -1
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +129 -2
- package/dist/repo/apps/play-runner-workers/src/entry.ts +147 -64
- package/dist/repo/apps/play-runner-workers/src/workflow-retry.ts +50 -0
- package/dist/repo/sdk/src/client.ts +46 -33
- package/dist/repo/sdk/src/config.ts +109 -233
- package/dist/repo/sdk/src/http.ts +44 -4
- package/dist/repo/sdk/src/index.ts +8 -5
- package/dist/repo/sdk/src/play.ts +124 -45
- package/dist/repo/sdk/src/plays/harness-stub.ts +2 -2
- package/dist/repo/sdk/src/tool-output.ts +26 -7
- package/dist/repo/sdk/src/types.ts +48 -12
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/run-failure.ts +49 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +138 -35
- package/package.json +1 -1
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
type ToolBatchRequest,
|
|
51
51
|
} from '../../../shared_libs/play-runtime/tool-batch-executor';
|
|
52
52
|
import {
|
|
53
|
+
adaptV2ExecuteResponseToToolResult,
|
|
53
54
|
createToolExecuteResult,
|
|
54
55
|
isToolExecuteResult,
|
|
55
56
|
type ToolExecuteResult,
|
|
@@ -118,6 +119,7 @@ import {
|
|
|
118
119
|
type CsvRenameOptions,
|
|
119
120
|
} from '../../../shared_libs/play-runtime/csv-rename';
|
|
120
121
|
import { coordinatorRequestHeaders } from '../../../shared_libs/play-runtime/coordinator-headers';
|
|
122
|
+
import { normalizePlayRunFailure } from '../../../shared_libs/play-runtime/run-failure';
|
|
121
123
|
import type {
|
|
122
124
|
LiveNodeProgressMap,
|
|
123
125
|
LiveNodeProgressSnapshot,
|
|
@@ -184,6 +186,100 @@ type WorkerFileRef = {
|
|
|
184
186
|
};
|
|
185
187
|
|
|
186
188
|
const EXECUTE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
|
|
189
|
+
const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
|
|
190
|
+
const V2_EXECUTE_RESPONSE_CONTRACT = 'v2-tool-execution-result';
|
|
191
|
+
|
|
192
|
+
class ToolHttpError extends Error {
|
|
193
|
+
readonly billing: Record<string, unknown> | null;
|
|
194
|
+
|
|
195
|
+
constructor(message: string, billing: Record<string, unknown> | null) {
|
|
196
|
+
super(message);
|
|
197
|
+
this.name = 'ToolHttpError';
|
|
198
|
+
this.billing = billing;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function formatCreditAmount(value: unknown): string {
|
|
203
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
204
|
+
return String(value ?? '-');
|
|
205
|
+
}
|
|
206
|
+
return Number(value.toFixed(8)).toString();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function getStringField(value: unknown, key: string): string | null {
|
|
210
|
+
if (!isRecord(value)) return null;
|
|
211
|
+
const field = value[key];
|
|
212
|
+
return typeof field === 'string' && field.trim() ? field : null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function getObjectField(value: unknown, key: string): Record<string, unknown> | null {
|
|
216
|
+
if (!isRecord(value)) return null;
|
|
217
|
+
const field = value[key];
|
|
218
|
+
return isRecord(field) ? field : null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function isInsufficientCreditsBilling(
|
|
222
|
+
billing: Record<string, unknown> | null,
|
|
223
|
+
): billing is Record<string, unknown> {
|
|
224
|
+
return billing?.kind === 'insufficient_credits';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function formatInsufficientCreditsMessage(input: {
|
|
228
|
+
billing: Record<string, unknown>;
|
|
229
|
+
toolId: string;
|
|
230
|
+
}): string {
|
|
231
|
+
const operation =
|
|
232
|
+
getStringField(input.billing, 'operation_id') ??
|
|
233
|
+
getStringField(input.billing, 'operation') ??
|
|
234
|
+
input.toolId;
|
|
235
|
+
const balance = formatCreditAmount(input.billing.balance_credits);
|
|
236
|
+
const required = formatCreditAmount(input.billing.required_credits);
|
|
237
|
+
const recommended = formatCreditAmount(
|
|
238
|
+
input.billing.recommended_add_credits ?? input.billing.needed_credits,
|
|
239
|
+
);
|
|
240
|
+
const billingUrl = getStringField(input.billing, 'billing_url');
|
|
241
|
+
const addSuffix =
|
|
242
|
+
billingUrl && recommended !== '-'
|
|
243
|
+
? ` Add >=${recommended} at ${billingUrl}.`
|
|
244
|
+
: billingUrl
|
|
245
|
+
? ` Add credits at ${billingUrl}.`
|
|
246
|
+
: '';
|
|
247
|
+
return `Workspace balance ${balance} < required ${required} for ${operation}.${addSuffix}`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function normalizeToolHttpErrorMessage(input: {
|
|
251
|
+
toolId: string;
|
|
252
|
+
status: number;
|
|
253
|
+
attempt: number;
|
|
254
|
+
maxAttempts: number;
|
|
255
|
+
bodyText: string;
|
|
256
|
+
}): ToolHttpError {
|
|
257
|
+
let parsed: Record<string, unknown> | null = null;
|
|
258
|
+
try {
|
|
259
|
+
const candidate = JSON.parse(input.bodyText);
|
|
260
|
+
parsed = isRecord(candidate) ? candidate : null;
|
|
261
|
+
} catch {
|
|
262
|
+
parsed = null;
|
|
263
|
+
}
|
|
264
|
+
const billing = getObjectField(parsed, 'billing');
|
|
265
|
+
if (isInsufficientCreditsBilling(billing)) {
|
|
266
|
+
return new ToolHttpError(
|
|
267
|
+
`tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${formatInsufficientCreditsMessage({
|
|
268
|
+
billing,
|
|
269
|
+
toolId: input.toolId,
|
|
270
|
+
})}`,
|
|
271
|
+
billing,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return new ToolHttpError(
|
|
275
|
+
`tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${input.bodyText.slice(0, 500)}`,
|
|
276
|
+
billing,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function extractErrorBilling(error: unknown): Record<string, unknown> | null {
|
|
281
|
+
return error instanceof ToolHttpError ? error.billing : null;
|
|
282
|
+
}
|
|
187
283
|
|
|
188
284
|
/** R2 binding injected by the Worker runtime (when present in deploy metadata). */
|
|
189
285
|
type WorkerEnv = {
|
|
@@ -983,40 +1079,25 @@ function isToolExecuteRecord(value: unknown): value is Record<string, unknown> {
|
|
|
983
1079
|
}
|
|
984
1080
|
|
|
985
1081
|
function normalizeToolExecuteArgs(
|
|
986
|
-
|
|
987
|
-
toolId?: unknown,
|
|
988
|
-
input?: unknown,
|
|
1082
|
+
request: unknown,
|
|
989
1083
|
): { id: string; toolId: string; input: Record<string, unknown> } {
|
|
990
|
-
if (isToolExecuteRecord(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
if (
|
|
995
|
-
typeof id !== 'string' ||
|
|
996
|
-
!id.trim() ||
|
|
997
|
-
typeof tool !== 'string' ||
|
|
998
|
-
!tool ||
|
|
999
|
-
!isToolExecuteRecord(requestInput)
|
|
1000
|
-
) {
|
|
1001
|
-
throw new Error(
|
|
1002
|
-
'ctx.tools.execute({ id, tool, input }) requires a non-empty id, tool string, and input object.',
|
|
1003
|
-
);
|
|
1004
|
-
}
|
|
1005
|
-
return { id: id.trim(), toolId: tool, input: requestInput };
|
|
1084
|
+
if (!isToolExecuteRecord(request)) {
|
|
1085
|
+
throw new Error(
|
|
1086
|
+
'ctx.tools.execute requires a request object: ctx.tools.execute({ id, tool, input, description }).',
|
|
1087
|
+
);
|
|
1006
1088
|
}
|
|
1007
|
-
|
|
1008
1089
|
if (
|
|
1009
|
-
typeof
|
|
1010
|
-
!
|
|
1011
|
-
typeof
|
|
1012
|
-
!
|
|
1013
|
-
!isToolExecuteRecord(input)
|
|
1090
|
+
typeof request.id !== 'string' ||
|
|
1091
|
+
!request.id.trim() ||
|
|
1092
|
+
typeof request.tool !== 'string' ||
|
|
1093
|
+
!request.tool.trim() ||
|
|
1094
|
+
!isToolExecuteRecord(request.input)
|
|
1014
1095
|
) {
|
|
1015
1096
|
throw new Error(
|
|
1016
|
-
'ctx.tools.execute(
|
|
1097
|
+
'ctx.tools.execute({ id, tool, input }) requires a non-empty id, tool string, and input object.',
|
|
1017
1098
|
);
|
|
1018
1099
|
}
|
|
1019
|
-
return { id:
|
|
1100
|
+
return { id: request.id.trim(), toolId: request.tool, input: request.input };
|
|
1020
1101
|
}
|
|
1021
1102
|
|
|
1022
1103
|
function integrationEventType(eventKey: string): string {
|
|
@@ -1149,13 +1230,14 @@ async function callToolDirect(
|
|
|
1149
1230
|
'content-type': 'application/json',
|
|
1150
1231
|
authorization: `Bearer ${req.executorToken}`,
|
|
1151
1232
|
'x-deepline-request-id': `${req.runId}:${toolId}:${id}:attempt:${attempt}`,
|
|
1233
|
+
[EXECUTE_RESPONSE_CONTRACT_HEADER]: V2_EXECUTE_RESPONSE_CONTRACT,
|
|
1152
1234
|
[EXECUTE_TOOL_METADATA_HEADER]: 'true',
|
|
1153
1235
|
},
|
|
1154
1236
|
body: JSON.stringify({ payload: input }),
|
|
1155
1237
|
});
|
|
1156
1238
|
if (res.ok) {
|
|
1157
1239
|
const body = (await res.json()) as Record<string, unknown>;
|
|
1158
|
-
const result = (body
|
|
1240
|
+
const { result } = adaptV2ExecuteResponseToToolResult(body);
|
|
1159
1241
|
const status =
|
|
1160
1242
|
typeof body.status === 'string'
|
|
1161
1243
|
? body.status
|
|
@@ -1171,9 +1253,13 @@ async function callToolDirect(
|
|
|
1171
1253
|
}
|
|
1172
1254
|
|
|
1173
1255
|
const text = await res.text().catch(() => '');
|
|
1174
|
-
lastError =
|
|
1175
|
-
|
|
1176
|
-
|
|
1256
|
+
lastError = normalizeToolHttpErrorMessage({
|
|
1257
|
+
toolId,
|
|
1258
|
+
status: res.status,
|
|
1259
|
+
attempt,
|
|
1260
|
+
maxAttempts,
|
|
1261
|
+
bodyText: text,
|
|
1262
|
+
});
|
|
1177
1263
|
const retryable =
|
|
1178
1264
|
res.status === 429 ||
|
|
1179
1265
|
(res.status >= 500 && WORKER_RETRY_SAFE_5XX_TOOLS.has(toolId));
|
|
@@ -1692,9 +1778,7 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
1692
1778
|
) => {
|
|
1693
1779
|
for (const entry of chunkResults) {
|
|
1694
1780
|
const batchResult = isToolExecuteResult(entry.result)
|
|
1695
|
-
?
|
|
1696
|
-
? entry.result.result.data
|
|
1697
|
-
: undefined
|
|
1781
|
+
? entry.result.toolOutput.raw
|
|
1698
1782
|
: entry.result;
|
|
1699
1783
|
const splitResults =
|
|
1700
1784
|
batchResult != null
|
|
@@ -1956,14 +2040,10 @@ async function executeWorkerWaterfall(
|
|
|
1956
2040
|
if (isWorkerInlineCodeStep(step)) {
|
|
1957
2041
|
result = await step.run(input, {
|
|
1958
2042
|
tools: {
|
|
1959
|
-
execute: async (
|
|
1960
|
-
requestOrKey: unknown,
|
|
1961
|
-
toolId?: unknown,
|
|
1962
|
-
toolInput?: unknown,
|
|
1963
|
-
) =>
|
|
2043
|
+
execute: async (request: unknown) =>
|
|
1964
2044
|
await executeToolWithLifecycle(
|
|
1965
2045
|
req,
|
|
1966
|
-
normalizeToolExecuteArgs(
|
|
2046
|
+
normalizeToolExecuteArgs(request),
|
|
1967
2047
|
workflowStep,
|
|
1968
2048
|
callbacks,
|
|
1969
2049
|
),
|
|
@@ -2731,6 +2811,7 @@ async function persistCompletedMapRows(input: {
|
|
|
2731
2811
|
await harnessPersistCompletedSheetRows({
|
|
2732
2812
|
baseUrl: input.req.baseUrl,
|
|
2733
2813
|
executorToken: input.req.executorToken,
|
|
2814
|
+
preloadedDbSessions: input.req.preloadedDbSessions ?? null,
|
|
2734
2815
|
playName: input.req.playName,
|
|
2735
2816
|
tableNamespace: input.tableNamespace,
|
|
2736
2817
|
sheetContract: augmentSheetContractWithDatasetFields({
|
|
@@ -2742,7 +2823,6 @@ async function persistCompletedMapRows(input: {
|
|
|
2742
2823
|
outputFields,
|
|
2743
2824
|
runId: input.req.runId,
|
|
2744
2825
|
userEmail: input.req.userEmail,
|
|
2745
|
-
preloadedDbSessions: input.req.preloadedDbSessions ?? null,
|
|
2746
2826
|
});
|
|
2747
2827
|
}
|
|
2748
2828
|
|
|
@@ -2762,6 +2842,7 @@ async function prepareMapRows(input: {
|
|
|
2762
2842
|
const result = await harnessStartSheetDataset({
|
|
2763
2843
|
baseUrl: input.req.baseUrl,
|
|
2764
2844
|
executorToken: input.req.executorToken,
|
|
2845
|
+
preloadedDbSessions: input.req.preloadedDbSessions ?? null,
|
|
2765
2846
|
playName: input.req.playName,
|
|
2766
2847
|
tableNamespace: input.tableNamespace,
|
|
2767
2848
|
sheetContract: augmentSheetContractWithDatasetFields({
|
|
@@ -2771,7 +2852,6 @@ async function prepareMapRows(input: {
|
|
|
2771
2852
|
rows: input.rows.map((row) => ({ ...row })),
|
|
2772
2853
|
runId: input.req.runId,
|
|
2773
2854
|
userEmail: input.req.userEmail,
|
|
2774
|
-
preloadedDbSessions: input.req.preloadedDbSessions ?? null,
|
|
2775
2855
|
});
|
|
2776
2856
|
for (const timing of result.timings ?? []) {
|
|
2777
2857
|
const phase =
|
|
@@ -3224,19 +3304,9 @@ function createMinimalWorkerCtx(
|
|
|
3224
3304
|
},
|
|
3225
3305
|
tools: {
|
|
3226
3306
|
...((ctx as { tools?: Record<string, unknown> }).tools ?? {}),
|
|
3227
|
-
execute: async (
|
|
3228
|
-
requestOrKey: unknown,
|
|
3229
|
-
toolId?: unknown,
|
|
3230
|
-
input?: unknown,
|
|
3231
|
-
_opts?: { description?: string },
|
|
3232
|
-
): Promise<unknown> => {
|
|
3233
|
-
void _opts;
|
|
3307
|
+
execute: async (requestArg: unknown): Promise<unknown> => {
|
|
3234
3308
|
assertNotAborted(abortSignal);
|
|
3235
|
-
const request = normalizeToolExecuteArgs(
|
|
3236
|
-
requestOrKey,
|
|
3237
|
-
toolId,
|
|
3238
|
-
input,
|
|
3239
|
-
);
|
|
3309
|
+
const request = normalizeToolExecuteArgs(requestArg);
|
|
3240
3310
|
return await toolBatchScheduler.execute(
|
|
3241
3311
|
request.id,
|
|
3242
3312
|
request.toolId,
|
|
@@ -3847,17 +3917,11 @@ function createMinimalWorkerCtx(
|
|
|
3847
3917
|
);
|
|
3848
3918
|
},
|
|
3849
3919
|
tools: {
|
|
3850
|
-
async execute(
|
|
3851
|
-
requestOrKey: unknown,
|
|
3852
|
-
toolId?: unknown,
|
|
3853
|
-
input?: unknown,
|
|
3854
|
-
_opts?: { description?: string },
|
|
3855
|
-
): Promise<unknown> {
|
|
3856
|
-
void _opts;
|
|
3920
|
+
async execute(requestArg: unknown): Promise<unknown> {
|
|
3857
3921
|
assertNotAborted(abortSignal);
|
|
3858
3922
|
return executeToolWithLifecycle(
|
|
3859
3923
|
req,
|
|
3860
|
-
normalizeToolExecuteArgs(
|
|
3924
|
+
normalizeToolExecuteArgs(requestArg),
|
|
3861
3925
|
workflowStep,
|
|
3862
3926
|
callbacks,
|
|
3863
3927
|
);
|
|
@@ -4646,7 +4710,9 @@ async function executeRunRequest(
|
|
|
4646
4710
|
error instanceof Error ? error.message : 'Play run cancelled.',
|
|
4647
4711
|
);
|
|
4648
4712
|
}
|
|
4649
|
-
const
|
|
4713
|
+
const failure = normalizePlayRunFailure(error);
|
|
4714
|
+
const message = failure.message;
|
|
4715
|
+
const errorBilling = extractErrorBilling(error);
|
|
4650
4716
|
if (options?.persistResultDatasets) {
|
|
4651
4717
|
appendRunLogLine(
|
|
4652
4718
|
`${aborted ? '[cancelled]' : '[error]'} ${redactSecretsFromLogString(message)}`,
|
|
@@ -4657,6 +4723,23 @@ async function executeRunRequest(
|
|
|
4657
4723
|
source: 'worker',
|
|
4658
4724
|
occurredAt: nowMs(),
|
|
4659
4725
|
error: message,
|
|
4726
|
+
result: aborted
|
|
4727
|
+
? undefined
|
|
4728
|
+
: {
|
|
4729
|
+
success: false,
|
|
4730
|
+
status: 'failed',
|
|
4731
|
+
error: message,
|
|
4732
|
+
errors: [
|
|
4733
|
+
{
|
|
4734
|
+
code: failure.code,
|
|
4735
|
+
phase: failure.phase,
|
|
4736
|
+
message,
|
|
4737
|
+
retryable: failure.retryable,
|
|
4738
|
+
...(errorBilling ? { billing: errorBilling } : {}),
|
|
4739
|
+
...(failure.cause ? { cause: failure.cause } : {}),
|
|
4740
|
+
},
|
|
4741
|
+
],
|
|
4742
|
+
},
|
|
4660
4743
|
});
|
|
4661
4744
|
await finalizeWorkerComputeBilling({
|
|
4662
4745
|
req,
|
|
@@ -4890,6 +4973,7 @@ async function persistResultDatasets(
|
|
|
4890
4973
|
await harnessStartSheetDataset({
|
|
4891
4974
|
baseUrl: req.baseUrl,
|
|
4892
4975
|
executorToken: req.executorToken,
|
|
4976
|
+
preloadedDbSessions: req.preloadedDbSessions ?? null,
|
|
4893
4977
|
playName: req.playName,
|
|
4894
4978
|
tableNamespace: dataset.tableNamespace,
|
|
4895
4979
|
sheetContract: requireSheetContract(req, dataset.tableNamespace),
|
|
@@ -4897,7 +4981,6 @@ async function persistResultDatasets(
|
|
|
4897
4981
|
runId: req.runId,
|
|
4898
4982
|
inputOffset: 0,
|
|
4899
4983
|
userEmail: req.userEmail,
|
|
4900
|
-
preloadedDbSessions: req.preloadedDbSessions ?? null,
|
|
4901
4984
|
});
|
|
4902
4985
|
}
|
|
4903
4986
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isCloudflareDurableObjectCodeUpdatedError,
|
|
3
|
+
normalizePlayRunFailure,
|
|
4
|
+
} from '../../../shared_libs/play-runtime/run-failure';
|
|
5
|
+
|
|
6
|
+
export const PLATFORM_DEPLOY_WORKFLOW_RETRY_LIMIT = 1;
|
|
7
|
+
|
|
8
|
+
export type WorkflowRetryDecision =
|
|
9
|
+
| {
|
|
10
|
+
action: 'retry';
|
|
11
|
+
reason: string;
|
|
12
|
+
nextAttempt: number;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
| {
|
|
16
|
+
action: 'fail';
|
|
17
|
+
reason: string | null;
|
|
18
|
+
message: string | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function decideWorkflowPlatformRetry(input: {
|
|
22
|
+
workflowStatus: string;
|
|
23
|
+
error: string | null;
|
|
24
|
+
retryAttempts: number;
|
|
25
|
+
}): WorkflowRetryDecision {
|
|
26
|
+
if (!input.error) {
|
|
27
|
+
return { action: 'fail', reason: null, message: null };
|
|
28
|
+
}
|
|
29
|
+
const retryablePlatformReset = isCloudflareDurableObjectCodeUpdatedError(
|
|
30
|
+
input.error,
|
|
31
|
+
);
|
|
32
|
+
if (
|
|
33
|
+
retryablePlatformReset &&
|
|
34
|
+
input.retryAttempts < PLATFORM_DEPLOY_WORKFLOW_RETRY_LIMIT
|
|
35
|
+
) {
|
|
36
|
+
return {
|
|
37
|
+
action: 'retry',
|
|
38
|
+
reason: 'cloudflare_durable_object_code_updated',
|
|
39
|
+
nextAttempt: input.retryAttempts + 1,
|
|
40
|
+
message: normalizePlayRunFailure(input.error).message,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
action: 'fail',
|
|
45
|
+
reason: retryablePlatformReset
|
|
46
|
+
? 'cloudflare_durable_object_code_updated_retry_exhausted'
|
|
47
|
+
: null,
|
|
48
|
+
message: normalizePlayRunFailure(input.error).message,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -67,6 +67,8 @@ import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-mani
|
|
|
67
67
|
|
|
68
68
|
const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
69
69
|
const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
|
|
70
|
+
const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
|
|
71
|
+
const V2_EXECUTE_RESPONSE_CONTRACT = 'v2-tool-execution-result';
|
|
70
72
|
const COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1_000];
|
|
71
73
|
|
|
72
74
|
function sleep(ms: number): Promise<void> {
|
|
@@ -95,9 +97,14 @@ type ExecuteToolRawOptions = {
|
|
|
95
97
|
export type ToolExecution<TData = unknown, TMeta = Record<string, unknown>> = {
|
|
96
98
|
status: string;
|
|
97
99
|
job_id?: string;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
toolExecutionResult: {
|
|
101
|
+
toolOutput: {
|
|
102
|
+
raw: TData;
|
|
103
|
+
meta?: TMeta;
|
|
104
|
+
};
|
|
105
|
+
extractedLists?: Record<string, unknown>;
|
|
106
|
+
extractedValues?: Record<string, unknown>;
|
|
107
|
+
meta?: Record<string, unknown>;
|
|
101
108
|
};
|
|
102
109
|
billing?: Record<string, unknown>;
|
|
103
110
|
[key: string]: unknown;
|
|
@@ -552,19 +559,22 @@ export class DeeplineClient {
|
|
|
552
559
|
/**
|
|
553
560
|
* Execute a tool and return the standard execution envelope.
|
|
554
561
|
*
|
|
555
|
-
* The `
|
|
556
|
-
* contains provider
|
|
562
|
+
* The `toolExecutionResult.toolOutput.raw` field contains the raw tool output.
|
|
563
|
+
* `toolExecutionResult.toolOutput.meta` contains tool/provider metadata.
|
|
557
564
|
* Top-level fields such as `status`, `job_id`, and `billing` describe the
|
|
558
|
-
* Deepline execution.
|
|
565
|
+
* Deepline execution envelope.
|
|
559
566
|
*/
|
|
560
567
|
async executeTool<TData = unknown, TMeta = Record<string, unknown>>(
|
|
561
568
|
toolId: string,
|
|
562
569
|
input: Record<string, unknown>,
|
|
563
570
|
options?: ExecuteToolRawOptions,
|
|
564
571
|
): Promise<ToolExecution<TData, TMeta>> {
|
|
565
|
-
const headers =
|
|
566
|
-
|
|
567
|
-
|
|
572
|
+
const headers = {
|
|
573
|
+
[EXECUTE_RESPONSE_CONTRACT_HEADER]: V2_EXECUTE_RESPONSE_CONTRACT,
|
|
574
|
+
...(options?.includeToolMetadata
|
|
575
|
+
? { [INCLUDE_TOOL_METADATA_HEADER]: 'true' }
|
|
576
|
+
: {}),
|
|
577
|
+
};
|
|
568
578
|
return this.http.post<ToolExecution<TData, TMeta>>(
|
|
569
579
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
570
580
|
{ payload: input },
|
|
@@ -1029,34 +1039,37 @@ export class DeeplineClient {
|
|
|
1029
1039
|
bytes: number;
|
|
1030
1040
|
}>,
|
|
1031
1041
|
): Promise<PlayStagedFileRef[]> {
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
'metadata',
|
|
1035
|
-
JSON.stringify({
|
|
1036
|
-
files: files.map((file, index) => ({
|
|
1037
|
-
index,
|
|
1038
|
-
logicalPath: file.logicalPath,
|
|
1039
|
-
contentHash: file.contentHash,
|
|
1040
|
-
contentType: file.contentType,
|
|
1041
|
-
bytes: file.bytes,
|
|
1042
|
-
})),
|
|
1043
|
-
}),
|
|
1044
|
-
);
|
|
1045
|
-
for (const [index, file] of files.entries()) {
|
|
1046
|
-
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1047
|
-
const body = bytes.buffer.slice(
|
|
1048
|
-
bytes.byteOffset,
|
|
1049
|
-
bytes.byteOffset + bytes.byteLength,
|
|
1050
|
-
) as ArrayBuffer;
|
|
1042
|
+
const buildFormData = () => {
|
|
1043
|
+
const formData = new FormData();
|
|
1051
1044
|
formData.set(
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1045
|
+
'metadata',
|
|
1046
|
+
JSON.stringify({
|
|
1047
|
+
files: files.map((file, index) => ({
|
|
1048
|
+
index,
|
|
1049
|
+
logicalPath: file.logicalPath,
|
|
1050
|
+
contentHash: file.contentHash,
|
|
1051
|
+
contentType: file.contentType,
|
|
1052
|
+
bytes: file.bytes,
|
|
1053
|
+
})),
|
|
1054
|
+
}),
|
|
1055
1055
|
);
|
|
1056
|
-
|
|
1056
|
+
for (const [index, file] of files.entries()) {
|
|
1057
|
+
const bytes = decodeBase64Bytes(file.contentBase64);
|
|
1058
|
+
const body = bytes.buffer.slice(
|
|
1059
|
+
bytes.byteOffset,
|
|
1060
|
+
bytes.byteOffset + bytes.byteLength,
|
|
1061
|
+
) as ArrayBuffer;
|
|
1062
|
+
formData.set(
|
|
1063
|
+
`file:${index}`,
|
|
1064
|
+
new Blob([body], { type: file.contentType }),
|
|
1065
|
+
file.logicalPath,
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
return formData;
|
|
1069
|
+
};
|
|
1057
1070
|
const response = await this.http.postFormData<{ files: PlayStagedFileRef[] }>(
|
|
1058
1071
|
'/api/v2/plays/files/stage',
|
|
1059
|
-
|
|
1072
|
+
buildFormData,
|
|
1060
1073
|
);
|
|
1061
1074
|
return response.files;
|
|
1062
1075
|
}
|