deepline 0.3.231 → 0.3.233
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/bundling-sources/sdk/src/client.ts +16 -523
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/tool-calls.ts +530 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +33 -415
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-http-request.ts +419 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-reliability-policy.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +20 -1
- package/dist/cli/index.js +831 -800
- package/dist/cli/index.mjs +831 -795
- package/dist/index.d.mts +191 -190
- package/dist/index.d.ts +191 -190
- package/dist/index.js +489 -472
- package/dist/index.mjs +489 -472
- package/dist/install-integrity.json +2 -0
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
PlaySettingsPathsPage,
|
|
5
5
|
} from '../../shared_libs/plays/settings-contract';
|
|
6
6
|
import { iterateCursorPages } from './pagination.js';
|
|
7
|
-
import type { ToolResultBilling } from '../../shared_libs/plays/tool-result-types.js';
|
|
8
7
|
/**
|
|
9
8
|
* The main API client for interacting with the Deepline platform.
|
|
10
9
|
*
|
|
@@ -41,7 +40,18 @@ import type { ToolResultBilling } from '../../shared_libs/plays/tool-result-type
|
|
|
41
40
|
* @module
|
|
42
41
|
*/
|
|
43
42
|
import { resolveConfig } from './config.js';
|
|
44
|
-
import { DeeplineError
|
|
43
|
+
import { DeeplineError } from './errors.js';
|
|
44
|
+
import {
|
|
45
|
+
ToolCalls,
|
|
46
|
+
type ExecuteToolRawOptions,
|
|
47
|
+
type ExecutionByKeyResult,
|
|
48
|
+
type ToolExecution,
|
|
49
|
+
} from './tool-calls.js';
|
|
50
|
+
export type {
|
|
51
|
+
ExecutionRecovery,
|
|
52
|
+
ExecutionByKeyResult,
|
|
53
|
+
ToolExecution,
|
|
54
|
+
} from './tool-calls.js';
|
|
45
55
|
import { HttpClient } from './http.js';
|
|
46
56
|
import { PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES } from '../../shared_libs/product-notifications/contract.js';
|
|
47
57
|
import {
|
|
@@ -112,12 +122,6 @@ import type { PlayStagedFileRef } from './plays/local-file-discovery.js';
|
|
|
112
122
|
import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-manifest.js';
|
|
113
123
|
import type { EnrichCompiledConfig } from './cli/enrich-play-compiler.js';
|
|
114
124
|
import { RUNTIME_ENVIRONMENT_TOKEN_HEADER } from '../../shared_libs/play-runtime/coordinator-headers.js';
|
|
115
|
-
import { resolveTheirstackClientTimeoutMs } from '../../shared_libs/integration-contracts/theirstack/execution-policy.js';
|
|
116
|
-
import { resolveWaterfallClientTimeoutMs } from '../../shared_libs/integration-contracts/waterfall/execution-policy.js';
|
|
117
|
-
import {
|
|
118
|
-
BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS,
|
|
119
|
-
usesExtendedBetterContactLauncherBudget,
|
|
120
|
-
} from '../../shared_libs/integration-contracts/bettercontact/execution-policy.js';
|
|
121
125
|
import {
|
|
122
126
|
normalizePlayRuntimeEnvironment,
|
|
123
127
|
normalizePlayRuntimeNamespace,
|
|
@@ -125,20 +129,10 @@ import {
|
|
|
125
129
|
normalizePlayRuntimeSelection,
|
|
126
130
|
type PlayRuntimeSelection,
|
|
127
131
|
} from '../../shared_libs/play-runtime/runtime-environment.js';
|
|
128
|
-
import {
|
|
129
|
-
TOOL_EXECUTION_ERROR_SCHEMA_HEADER,
|
|
130
|
-
TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
131
|
-
} from '../../shared_libs/plays/tool-execution-error';
|
|
132
132
|
import {
|
|
133
133
|
decodePlayRunPublicStatus,
|
|
134
134
|
type PlayRunPublicStatus,
|
|
135
135
|
} from '../../shared_libs/play-runtime/run-lifecycle-policy.js';
|
|
136
|
-
import {
|
|
137
|
-
legacyRawFromToolResponseRawV2,
|
|
138
|
-
providerMetaFromToolResponseRawV2,
|
|
139
|
-
RAW_V2_TOOL_RESPONSE_CONTRACT,
|
|
140
|
-
} from '../../shared_libs/plays/tool-response-contract.js';
|
|
141
|
-
|
|
142
136
|
export type SlackNotificationTarget =
|
|
143
137
|
| { channel: string; memberId?: never }
|
|
144
138
|
| { channel?: never; memberId: string };
|
|
@@ -224,127 +218,14 @@ function notificationObjectKey(kind: 'group' | 'destination') {
|
|
|
224
218
|
}
|
|
225
219
|
|
|
226
220
|
const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
227
|
-
const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
|
|
228
|
-
const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
|
|
229
|
-
const EXECUTE_RESPONSE_INTENT_HEADER = 'x-deepline-execute-response-intent';
|
|
230
221
|
const MONITOR_NON_RETRYABLE_MUTATION_OPTIONS = {
|
|
231
222
|
maxRetries: 0,
|
|
232
223
|
exactUrlOnly: true,
|
|
233
224
|
};
|
|
234
|
-
const RAW_V2_EXECUTE_RESPONSE_CONTRACT = RAW_V2_TOOL_RESPONSE_CONTRACT;
|
|
235
|
-
const DEFAULT_EXECUTION_RECOVERY_TIMEOUT_MS = 15 * 60 * 1_000;
|
|
236
|
-
|
|
237
|
-
function validateExecutionIdempotencyKey(key: string): void {
|
|
238
|
-
if (
|
|
239
|
-
typeof key !== 'string' ||
|
|
240
|
-
key.length < 1 ||
|
|
241
|
-
key.length > 200 ||
|
|
242
|
-
!/^[A-Za-z0-9._:-]+$/.test(key)
|
|
243
|
-
) {
|
|
244
|
-
throw new DeeplineError(
|
|
245
|
-
'Execution idempotency keys must be 1–200 ASCII letters, digits, dots, underscores, colons, or hyphens.',
|
|
246
|
-
undefined,
|
|
247
|
-
'IDEMPOTENCY_KEY_INVALID',
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function isExecutionInProgressResponse(value: unknown): boolean {
|
|
253
|
-
return (
|
|
254
|
-
typeof value === 'object' &&
|
|
255
|
-
value !== null &&
|
|
256
|
-
typeof (value as { executionRecovery?: { state?: unknown } })
|
|
257
|
-
.executionRecovery === 'object' &&
|
|
258
|
-
(value as { executionRecovery: { state?: unknown } }).executionRecovery
|
|
259
|
-
.state === 'running'
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function getExecutionRecoveryState(
|
|
264
|
-
value: unknown,
|
|
265
|
-
): ExecutionRecovery['state'] | null {
|
|
266
|
-
if (
|
|
267
|
-
typeof value !== 'object' ||
|
|
268
|
-
value === null ||
|
|
269
|
-
typeof (value as { executionRecovery?: { state?: unknown } })
|
|
270
|
-
.executionRecovery !== 'object'
|
|
271
|
-
) {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
const state = (value as { executionRecovery: { state?: unknown } })
|
|
275
|
-
.executionRecovery.state;
|
|
276
|
-
return state === 'running' ||
|
|
277
|
-
state === 'completed' ||
|
|
278
|
-
state === 'outcome_unknown'
|
|
279
|
-
? state
|
|
280
|
-
: null;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function isRecoverableExecutionAttemptError(error: unknown): boolean {
|
|
284
|
-
if (error instanceof ToolExecutionError) {
|
|
285
|
-
return (
|
|
286
|
-
error.code === 'EXECUTION_IN_PROGRESS' ||
|
|
287
|
-
(error.origin === 'deepline' &&
|
|
288
|
-
error.category === 'network' &&
|
|
289
|
-
error.networkScope === 'client_to_deepline')
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
return (
|
|
293
|
-
error instanceof DeeplineError &&
|
|
294
|
-
(error.code === 'EXECUTION_IN_PROGRESS' ||
|
|
295
|
-
(error.code?.startsWith('NETWORK_') === true &&
|
|
296
|
-
error.statusCode === undefined))
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function isRecoverableExecutionLookupError(error: unknown): boolean {
|
|
301
|
-
if (isRecoverableExecutionAttemptError(error)) return true;
|
|
302
|
-
return (
|
|
303
|
-
error instanceof DeeplineError &&
|
|
304
|
-
(error.statusCode === 429 ||
|
|
305
|
-
(error.statusCode !== undefined && error.statusCode >= 500))
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function timeoutWithinRecoveryBudget(
|
|
310
|
-
requestTimeoutMs: number | undefined,
|
|
311
|
-
defaultRequestTimeoutMs: number,
|
|
312
|
-
remainingMs: number,
|
|
313
|
-
): number {
|
|
314
|
-
return Math.min(requestTimeoutMs ?? defaultRequestTimeoutMs, remainingMs);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function executionRecoveryError(input: {
|
|
318
|
-
toolId: string;
|
|
319
|
-
idempotencyKey: string;
|
|
320
|
-
code:
|
|
321
|
-
| 'EXECUTION_IN_PROGRESS'
|
|
322
|
-
| 'EXECUTION_OUTCOME_UNKNOWN'
|
|
323
|
-
| 'EXECUTION_RECOVERY_TIMEOUT';
|
|
324
|
-
message: string;
|
|
325
|
-
}): ToolExecutionError {
|
|
326
|
-
return new ToolExecutionError(input.message, {
|
|
327
|
-
toolId: input.toolId,
|
|
328
|
-
provider: null,
|
|
329
|
-
operation: input.toolId,
|
|
330
|
-
code: input.code,
|
|
331
|
-
origin: 'deepline',
|
|
332
|
-
category:
|
|
333
|
-
input.code === 'EXECUTION_OUTCOME_UNKNOWN' ? 'unknown' : 'conflict',
|
|
334
|
-
retryable: input.code !== 'EXECUTION_OUTCOME_UNKNOWN',
|
|
335
|
-
statusCode: null,
|
|
336
|
-
requestId: null,
|
|
337
|
-
retryAfterMs: null,
|
|
338
|
-
networkKind: null,
|
|
339
|
-
networkScope: null,
|
|
340
|
-
publicDetails: { idempotencyKey: input.idempotencyKey },
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
225
|
const COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1_000];
|
|
344
226
|
const REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
|
|
345
227
|
const REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
|
|
346
228
|
const REGISTER_PLAY_ARTIFACTS_MAX_BATCH_BYTES = 2_500_000;
|
|
347
|
-
const DEEPLINEAGENT_EXECUTE_TIMEOUT_MS = 15 * 60 * 1000;
|
|
348
229
|
|
|
349
230
|
export type IngestionStorageRepairResult = {
|
|
350
231
|
status?: 'repaired';
|
|
@@ -659,124 +540,6 @@ function chunkRegisterPlayArtifacts<T>(artifacts: T[]): T[][] {
|
|
|
659
540
|
return chunks;
|
|
660
541
|
}
|
|
661
542
|
|
|
662
|
-
type ExecuteToolRawOptions = {
|
|
663
|
-
includeToolMetadata?: boolean;
|
|
664
|
-
responseIntent?: 'dataset' | 'raw' | 'row_artifact';
|
|
665
|
-
metadata?: Record<string, unknown>;
|
|
666
|
-
timeout?: number;
|
|
667
|
-
maxRetries?: number;
|
|
668
|
-
/** Retry transient HTTP API responses when the caller has confirmed safety. */
|
|
669
|
-
retryApiErrors?: boolean;
|
|
670
|
-
/** Reuse this stable key to recover or retry an ambiguous execution. */
|
|
671
|
-
idempotencyKey?: string;
|
|
672
|
-
/** Generate a key and enable recoverable execution before dispatch. */
|
|
673
|
-
recover?: boolean;
|
|
674
|
-
/** Called and awaited with the key before the first network request. */
|
|
675
|
-
onExecution?: (execution: { idempotencyKey: string }) => void | Promise<void>;
|
|
676
|
-
/** Maximum time to reconnect to an in-progress keyed execution. Defaults to 15 minutes. */
|
|
677
|
-
recoveryTimeoutMs?: number;
|
|
678
|
-
/** The `search_ref` of the tools search that found this tool. */
|
|
679
|
-
searchRef?: string;
|
|
680
|
-
};
|
|
681
|
-
|
|
682
|
-
/** Durable state returned for a keyed tool execution. */
|
|
683
|
-
export type ExecutionRecovery = {
|
|
684
|
-
/** Stable caller key used to resume or replay this execution. */
|
|
685
|
-
idempotencyKey: string;
|
|
686
|
-
/** Whether the execution is still running, completed, or has an unknown provider outcome. */
|
|
687
|
-
state: 'running' | 'completed' | 'outcome_unknown';
|
|
688
|
-
/** Whether this response came from an existing durable execution. */
|
|
689
|
-
replayed: boolean;
|
|
690
|
-
/** ISO timestamp after which the completed execution can no longer be replayed. */
|
|
691
|
-
expiresAt?: string;
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
/** Durable lookup result for a keyed tool execution. */
|
|
695
|
-
export type ExecutionByKeyResult = {
|
|
696
|
-
/** Recovery state and the key that owns the execution. */
|
|
697
|
-
executionRecovery: ExecutionRecovery;
|
|
698
|
-
/** Provider tool associated with the execution. */
|
|
699
|
-
toolId: string;
|
|
700
|
-
/** Original server-owned billing request ID, allocated before provider dispatch. */
|
|
701
|
-
requestId?: string;
|
|
702
|
-
/** HTTP status saved with the original response, when it was terminal. */
|
|
703
|
-
responseStatus?: number;
|
|
704
|
-
/** Saved execution response, when one is available for replay. */
|
|
705
|
-
response?: unknown;
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* The server launches the Apify run, polls it for `payload.timeoutMs`, then
|
|
710
|
-
* reads its dataset before replying. Keep the client request alive through
|
|
711
|
-
* that final hand-off so a pollable run id is never lost to the SDK default
|
|
712
|
-
* timeout.
|
|
713
|
-
*/
|
|
714
|
-
const APIFY_SYNC_DEFAULT_TIMEOUT_MS = 300_000;
|
|
715
|
-
const APIFY_SYNC_RESPONSE_GRACE_MS = 90_000;
|
|
716
|
-
function resolveToolExecuteTimeoutMs(
|
|
717
|
-
toolId: string,
|
|
718
|
-
input: Record<string, unknown>,
|
|
719
|
-
): number | undefined {
|
|
720
|
-
const normalized = toolId.trim().toLowerCase();
|
|
721
|
-
if (normalized === 'apify_run_actor_sync') {
|
|
722
|
-
const requestedTimeoutMs = input.timeoutMs ?? APIFY_SYNC_DEFAULT_TIMEOUT_MS;
|
|
723
|
-
if (
|
|
724
|
-
typeof requestedTimeoutMs === 'number' &&
|
|
725
|
-
Number.isFinite(requestedTimeoutMs) &&
|
|
726
|
-
requestedTimeoutMs > 0
|
|
727
|
-
) {
|
|
728
|
-
return Math.floor(requestedTimeoutMs) + APIFY_SYNC_RESPONSE_GRACE_MS;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
// Provider-specific slow-request policies live in shared modules so the SDK
|
|
732
|
-
// and server classify the same payloads. If more providers need this, replace
|
|
733
|
-
// these individual checks with a shared policy registry, not a wider default.
|
|
734
|
-
const theirstackTimeoutMs = resolveTheirstackClientTimeoutMs(
|
|
735
|
-
normalized,
|
|
736
|
-
input,
|
|
737
|
-
);
|
|
738
|
-
if (theirstackTimeoutMs !== null) return theirstackTimeoutMs;
|
|
739
|
-
const waterfallTimeoutMs = resolveWaterfallClientTimeoutMs(normalized);
|
|
740
|
-
if (waterfallTimeoutMs !== null) return waterfallTimeoutMs;
|
|
741
|
-
if (usesExtendedBetterContactLauncherBudget(normalized)) {
|
|
742
|
-
return BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS;
|
|
743
|
-
}
|
|
744
|
-
return normalized === 'deeplineagent' ||
|
|
745
|
-
normalized === 'deeplineagent_deeplineagent' ||
|
|
746
|
-
normalized === 'ai_inference' ||
|
|
747
|
-
normalized === 'deeplineagent_ai_inference' ||
|
|
748
|
-
normalized === 'ai_evaluate' ||
|
|
749
|
-
normalized === 'deeplineagent_ai_evaluate' ||
|
|
750
|
-
normalized === 'aiinference'
|
|
751
|
-
? DEEPLINEAGENT_EXECUTE_TIMEOUT_MS
|
|
752
|
-
: undefined;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* Standard provider/tool execution envelope returned by low-level SDK calls.
|
|
757
|
-
*
|
|
758
|
-
* `toolResponse.rawV2` contains the complete scrubbed provider response;
|
|
759
|
-
* `toolResponse.raw` is derived locally as the legacy provider-result projection. `extractedValues` and
|
|
760
|
-
* `extractedLists` contain Deepline-normalized getters when the tool exposes
|
|
761
|
-
* them. Billing fields are Deepline-facing and must not expose provider spend.
|
|
762
|
-
*/
|
|
763
|
-
export type ToolExecution<TData = unknown, TMeta = Record<string, unknown>> = {
|
|
764
|
-
status: string;
|
|
765
|
-
job_id?: string;
|
|
766
|
-
meta?: Record<string, unknown>;
|
|
767
|
-
toolResponse: {
|
|
768
|
-
raw: TData;
|
|
769
|
-
rawV2?: unknown;
|
|
770
|
-
view?: 'data' | 'rawV2';
|
|
771
|
-
meta?: TMeta;
|
|
772
|
-
responseMeta?: TMeta;
|
|
773
|
-
};
|
|
774
|
-
extractedLists?: Record<string, unknown>;
|
|
775
|
-
extractedValues?: Record<string, unknown>;
|
|
776
|
-
billing?: ToolResultBilling;
|
|
777
|
-
[key: string]: unknown;
|
|
778
|
-
};
|
|
779
|
-
|
|
780
543
|
/** Filters for `client.runs.list(...)`. */
|
|
781
544
|
export type RunsListOptions = {
|
|
782
545
|
play?: string;
|
|
@@ -2416,67 +2179,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
2416
2179
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
2417
2180
|
}
|
|
2418
2181
|
|
|
2419
|
-
/** Materialize raw-v2's rawV2-only wire response into the stable SDK result view. */
|
|
2420
|
-
function materializeToolExecutionResponse<
|
|
2421
|
-
TData = unknown,
|
|
2422
|
-
TMeta = Record<string, unknown>,
|
|
2423
|
-
>(response: ToolExecution<TData, TMeta>): ToolExecution<TData, TMeta> {
|
|
2424
|
-
const toolResponse = response.toolResponse;
|
|
2425
|
-
// An older V2 backend does not understand raw-v2 yet and therefore falls
|
|
2426
|
-
// through to the historical `{ result: { data, meta? } }` response. New SDK
|
|
2427
|
-
// clients must keep working during that server rollout window.
|
|
2428
|
-
if (!toolResponse && isRecord(response.result)) {
|
|
2429
|
-
const legacyResult = response.result;
|
|
2430
|
-
if (Object.prototype.hasOwnProperty.call(legacyResult, 'data')) {
|
|
2431
|
-
const legacyMeta = isRecord(legacyResult.meta)
|
|
2432
|
-
? (legacyResult.meta as TMeta)
|
|
2433
|
-
: undefined;
|
|
2434
|
-
return {
|
|
2435
|
-
...response,
|
|
2436
|
-
toolResponse: {
|
|
2437
|
-
raw: legacyResult.data as TData,
|
|
2438
|
-
...(legacyMeta ? { meta: legacyMeta } : {}),
|
|
2439
|
-
},
|
|
2440
|
-
};
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
if (
|
|
2444
|
-
!toolResponse ||
|
|
2445
|
-
Object.prototype.hasOwnProperty.call(toolResponse, 'raw')
|
|
2446
|
-
) {
|
|
2447
|
-
return response;
|
|
2448
|
-
}
|
|
2449
|
-
const rawV2 = toolResponse.rawV2;
|
|
2450
|
-
const view = toolResponse.view;
|
|
2451
|
-
const providerMeta = providerMetaFromToolResponseRawV2(
|
|
2452
|
-
rawV2,
|
|
2453
|
-
view ?? 'rawV2',
|
|
2454
|
-
) as TMeta | undefined;
|
|
2455
|
-
const responseMeta = isRecord(toolResponse.responseMeta)
|
|
2456
|
-
? (toolResponse.responseMeta as TMeta)
|
|
2457
|
-
: undefined;
|
|
2458
|
-
return {
|
|
2459
|
-
...response,
|
|
2460
|
-
toolResponse: {
|
|
2461
|
-
...toolResponse,
|
|
2462
|
-
raw: legacyRawFromToolResponseRawV2(
|
|
2463
|
-
rawV2,
|
|
2464
|
-
view ?? 'rawV2',
|
|
2465
|
-
responseMeta as Record<string, unknown> | undefined,
|
|
2466
|
-
) as TData,
|
|
2467
|
-
...(toolResponse.meta || providerMeta || responseMeta
|
|
2468
|
-
? {
|
|
2469
|
-
meta: {
|
|
2470
|
-
...(toolResponse.meta ?? {}),
|
|
2471
|
-
...(providerMeta ?? {}),
|
|
2472
|
-
...(responseMeta ?? {}),
|
|
2473
|
-
} as TMeta,
|
|
2474
|
-
}
|
|
2475
|
-
: {}),
|
|
2476
|
-
},
|
|
2477
|
-
};
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
2182
|
function isPrebuiltPlayDescription(
|
|
2481
2183
|
play: Pick<PlayDescription, 'origin' | 'ownerType'>,
|
|
2482
2184
|
): boolean {
|
|
@@ -2926,6 +2628,7 @@ function playRunResultFromStatus(
|
|
|
2926
2628
|
*/
|
|
2927
2629
|
export class DeeplineClient {
|
|
2928
2630
|
private readonly http: HttpClient;
|
|
2631
|
+
private readonly toolCalls: ToolCalls;
|
|
2929
2632
|
private readonly config: ResolvedConfig;
|
|
2930
2633
|
/** Canonical run lifecycle namespace backed by `/api/v2/runs`. */
|
|
2931
2634
|
readonly runs: RunsNamespace;
|
|
@@ -2950,6 +2653,7 @@ export class DeeplineClient {
|
|
|
2950
2653
|
constructor(options?: DeeplineClientOptions) {
|
|
2951
2654
|
this.config = resolveConfig(options);
|
|
2952
2655
|
this.http = new HttpClient(this.config);
|
|
2656
|
+
this.toolCalls = new ToolCalls(this.http, this.config.timeout);
|
|
2953
2657
|
this.runs = {
|
|
2954
2658
|
get: (runId, options) => this.getRunStatus(runId, options),
|
|
2955
2659
|
status: (runId) => this.getRunLifecycleStatus(runId),
|
|
@@ -3493,225 +3197,14 @@ export class DeeplineClient {
|
|
|
3493
3197
|
input: Record<string, unknown>,
|
|
3494
3198
|
options?: ExecuteToolRawOptions,
|
|
3495
3199
|
): Promise<ToolExecution<TData, TMeta>> {
|
|
3496
|
-
|
|
3497
|
-
// before any retry, so every POST is byte-equivalent in meaning.
|
|
3498
|
-
const inputSnapshot = JSON.parse(JSON.stringify(input)) as Record<
|
|
3499
|
-
string,
|
|
3500
|
-
unknown
|
|
3501
|
-
>;
|
|
3502
|
-
const metadataSnapshot = options?.metadata
|
|
3503
|
-
? (JSON.parse(JSON.stringify(options.metadata)) as Record<
|
|
3504
|
-
string,
|
|
3505
|
-
unknown
|
|
3506
|
-
>)
|
|
3507
|
-
: undefined;
|
|
3508
|
-
const idempotencyKey =
|
|
3509
|
-
options?.idempotencyKey ??
|
|
3510
|
-
(options?.recover ? crypto.randomUUID() : undefined);
|
|
3511
|
-
if (idempotencyKey !== undefined) {
|
|
3512
|
-
validateExecutionIdempotencyKey(idempotencyKey);
|
|
3513
|
-
if (
|
|
3514
|
-
options?.recoveryTimeoutMs !== undefined &&
|
|
3515
|
-
(!Number.isFinite(options.recoveryTimeoutMs) ||
|
|
3516
|
-
options.recoveryTimeoutMs < 0)
|
|
3517
|
-
) {
|
|
3518
|
-
throw new DeeplineError(
|
|
3519
|
-
'recoveryTimeoutMs must be a finite, non-negative number.',
|
|
3520
|
-
undefined,
|
|
3521
|
-
'IDEMPOTENCY_KEY_INVALID',
|
|
3522
|
-
);
|
|
3523
|
-
}
|
|
3524
|
-
await options?.onExecution?.({ idempotencyKey });
|
|
3525
|
-
const timeoutMs =
|
|
3526
|
-
options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
|
|
3527
|
-
await this.lookupExecutionByKey(idempotencyKey, timeoutMs);
|
|
3528
|
-
}
|
|
3529
|
-
const timeoutMs =
|
|
3530
|
-
options?.timeout ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
|
|
3531
|
-
const isApifySync = toolId.trim().toLowerCase() === 'apify_run_actor_sync';
|
|
3532
|
-
const headers = {
|
|
3533
|
-
[EXECUTE_RESPONSE_CONTRACT_HEADER]: RAW_V2_EXECUTE_RESPONSE_CONTRACT,
|
|
3534
|
-
...(isApifySync
|
|
3535
|
-
? { 'X-Deepline-Apify-Sync-Response-Budget': 'extended-v1' }
|
|
3536
|
-
: {}),
|
|
3537
|
-
[TOOL_EXECUTION_ERROR_SCHEMA_HEADER]: String(
|
|
3538
|
-
TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
3539
|
-
),
|
|
3540
|
-
...(options?.includeToolMetadata
|
|
3541
|
-
? { [INCLUDE_TOOL_METADATA_HEADER]: 'true' }
|
|
3542
|
-
: {}),
|
|
3543
|
-
[EXECUTE_RESPONSE_INTENT_HEADER]: options?.responseIntent ?? 'raw',
|
|
3544
|
-
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
|
|
3545
|
-
...(options?.searchRef
|
|
3546
|
-
? { 'x-deepline-search-ref': options.searchRef }
|
|
3547
|
-
: {}),
|
|
3548
|
-
};
|
|
3549
|
-
const request = (requestTimeoutMs: number | undefined) =>
|
|
3550
|
-
this.http.post<ToolExecution<TData, TMeta>>(
|
|
3551
|
-
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
3552
|
-
{
|
|
3553
|
-
payload: inputSnapshot,
|
|
3554
|
-
...(metadataSnapshot ? { metadata: metadataSnapshot } : {}),
|
|
3555
|
-
},
|
|
3556
|
-
headers,
|
|
3557
|
-
{
|
|
3558
|
-
timeout: requestTimeoutMs,
|
|
3559
|
-
maxRetries: idempotencyKey ? 0 : (options?.maxRetries ?? 0),
|
|
3560
|
-
retryApiErrors: options?.retryApiErrors === true,
|
|
3561
|
-
exactUrlOnly: true,
|
|
3562
|
-
toolId,
|
|
3563
|
-
idempotencyKey,
|
|
3564
|
-
},
|
|
3565
|
-
);
|
|
3566
|
-
let response: ToolExecution<TData, TMeta>;
|
|
3567
|
-
let recoveryDeadline: number | null = null;
|
|
3568
|
-
const recoveryTimeoutMs =
|
|
3569
|
-
options?.recoveryTimeoutMs ?? DEFAULT_EXECUTION_RECOVERY_TIMEOUT_MS;
|
|
3570
|
-
let retryDelayMs = 1_000;
|
|
3571
|
-
const recoveryTimeoutError = () =>
|
|
3572
|
-
executionRecoveryError({
|
|
3573
|
-
toolId,
|
|
3574
|
-
idempotencyKey: idempotencyKey!,
|
|
3575
|
-
code: 'EXECUTION_RECOVERY_TIMEOUT',
|
|
3576
|
-
message: `Execution recovery for ${idempotencyKey} did not finish before the recovery timeout. Inspect client.executions.getByKey() and resume with the same idempotency key.`,
|
|
3577
|
-
});
|
|
3578
|
-
const waitForRecoveryRetry = async (deadline: number) => {
|
|
3579
|
-
const remainingMs = deadline - Date.now();
|
|
3580
|
-
if (remainingMs <= 0) throw recoveryTimeoutError();
|
|
3581
|
-
await new Promise((resolve) =>
|
|
3582
|
-
setTimeout(resolve, Math.min(retryDelayMs, remainingMs)),
|
|
3583
|
-
);
|
|
3584
|
-
retryDelayMs = Math.min(retryDelayMs * 2, 5_000);
|
|
3585
|
-
};
|
|
3586
|
-
const lookupDuringRecovery = async () => {
|
|
3587
|
-
while (true) {
|
|
3588
|
-
const remainingMs = recoveryDeadline! - Date.now();
|
|
3589
|
-
if (remainingMs <= 0) throw recoveryTimeoutError();
|
|
3590
|
-
try {
|
|
3591
|
-
return await this.lookupExecutionByKey(
|
|
3592
|
-
idempotencyKey!,
|
|
3593
|
-
timeoutWithinRecoveryBudget(
|
|
3594
|
-
timeoutMs,
|
|
3595
|
-
this.config.timeout,
|
|
3596
|
-
remainingMs,
|
|
3597
|
-
),
|
|
3598
|
-
);
|
|
3599
|
-
} catch (error) {
|
|
3600
|
-
if (!isRecoverableExecutionLookupError(error)) throw error;
|
|
3601
|
-
await waitForRecoveryRetry(recoveryDeadline!);
|
|
3602
|
-
}
|
|
3603
|
-
}
|
|
3604
|
-
};
|
|
3605
|
-
while (true) {
|
|
3606
|
-
const remainingMs =
|
|
3607
|
-
recoveryDeadline === null ? null : recoveryDeadline - Date.now();
|
|
3608
|
-
if (remainingMs !== null && remainingMs <= 0) {
|
|
3609
|
-
throw recoveryTimeoutError();
|
|
3610
|
-
}
|
|
3611
|
-
try {
|
|
3612
|
-
response = await request(
|
|
3613
|
-
remainingMs === null
|
|
3614
|
-
? timeoutMs
|
|
3615
|
-
: timeoutWithinRecoveryBudget(
|
|
3616
|
-
timeoutMs,
|
|
3617
|
-
this.config.timeout,
|
|
3618
|
-
remainingMs,
|
|
3619
|
-
),
|
|
3620
|
-
);
|
|
3621
|
-
if (getExecutionRecoveryState(response) === 'outcome_unknown') {
|
|
3622
|
-
if (!idempotencyKey) break;
|
|
3623
|
-
throw executionRecoveryError({
|
|
3624
|
-
toolId,
|
|
3625
|
-
idempotencyKey,
|
|
3626
|
-
code: 'EXECUTION_OUTCOME_UNKNOWN',
|
|
3627
|
-
message: `The outcome of execution ${idempotencyKey} is unknown. Do not retry with a new key; inspect client.executions.getByKey().`,
|
|
3628
|
-
});
|
|
3629
|
-
}
|
|
3630
|
-
if (!idempotencyKey || !isExecutionInProgressResponse(response)) {
|
|
3631
|
-
break;
|
|
3632
|
-
}
|
|
3633
|
-
if (recoveryDeadline === null) {
|
|
3634
|
-
recoveryDeadline = Date.now() + recoveryTimeoutMs;
|
|
3635
|
-
}
|
|
3636
|
-
} catch (error) {
|
|
3637
|
-
if (
|
|
3638
|
-
idempotencyKey &&
|
|
3639
|
-
error instanceof DeeplineError &&
|
|
3640
|
-
error.code === 'EXECUTION_OUTCOME_UNKNOWN'
|
|
3641
|
-
) {
|
|
3642
|
-
throw executionRecoveryError({
|
|
3643
|
-
toolId,
|
|
3644
|
-
idempotencyKey,
|
|
3645
|
-
code: 'EXECUTION_OUTCOME_UNKNOWN',
|
|
3646
|
-
message: error.message,
|
|
3647
|
-
});
|
|
3648
|
-
}
|
|
3649
|
-
if (!idempotencyKey || !isRecoverableExecutionAttemptError(error)) {
|
|
3650
|
-
throw error;
|
|
3651
|
-
}
|
|
3652
|
-
if (recoveryDeadline === null) {
|
|
3653
|
-
recoveryDeadline = Date.now() + recoveryTimeoutMs;
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
await waitForRecoveryRetry(recoveryDeadline!);
|
|
3657
|
-
const recovered = await lookupDuringRecovery();
|
|
3658
|
-
if (getExecutionRecoveryState(recovered) === 'outcome_unknown') {
|
|
3659
|
-
throw executionRecoveryError({
|
|
3660
|
-
toolId,
|
|
3661
|
-
idempotencyKey,
|
|
3662
|
-
code: 'EXECUTION_OUTCOME_UNKNOWN',
|
|
3663
|
-
message: `The outcome of execution ${idempotencyKey} is unknown. Do not retry with a new key; inspect client.executions.getByKey().`,
|
|
3664
|
-
});
|
|
3665
|
-
}
|
|
3666
|
-
}
|
|
3667
|
-
const materialized = materializeToolExecutionResponse(response);
|
|
3668
|
-
return idempotencyKey ? { ...materialized, idempotencyKey } : materialized;
|
|
3200
|
+
return this.toolCalls.call<TData, TMeta>(toolId, input, options);
|
|
3669
3201
|
}
|
|
3670
3202
|
|
|
3671
3203
|
/** Read the durable state of a keyed tool execution. */
|
|
3672
3204
|
async getExecutionByKey(
|
|
3673
3205
|
idempotencyKey: string,
|
|
3674
3206
|
): Promise<ExecutionByKeyResult> {
|
|
3675
|
-
|
|
3676
|
-
const result = await this.lookupExecutionByKey(idempotencyKey);
|
|
3677
|
-
if (!result) {
|
|
3678
|
-
throw new DeeplineError(
|
|
3679
|
-
`No execution exists for idempotency key ${idempotencyKey}.`,
|
|
3680
|
-
404,
|
|
3681
|
-
'EXECUTION_NOT_FOUND',
|
|
3682
|
-
);
|
|
3683
|
-
}
|
|
3684
|
-
return result;
|
|
3685
|
-
}
|
|
3686
|
-
|
|
3687
|
-
private async lookupExecutionByKey(
|
|
3688
|
-
idempotencyKey: string,
|
|
3689
|
-
timeoutMs?: number,
|
|
3690
|
-
): Promise<ExecutionByKeyResult | null> {
|
|
3691
|
-
validateExecutionIdempotencyKey(idempotencyKey);
|
|
3692
|
-
let supported = false;
|
|
3693
|
-
const result = await this.http.get<ExecutionByKeyResult | null>(
|
|
3694
|
-
`/api/v2/executions/by-key/${encodeURIComponent(idempotencyKey)}`,
|
|
3695
|
-
{
|
|
3696
|
-
exactUrlOnly: true,
|
|
3697
|
-
maxRetries: 0,
|
|
3698
|
-
timeout: timeoutMs,
|
|
3699
|
-
idempotencyKey,
|
|
3700
|
-
allowNotFound: true,
|
|
3701
|
-
onResponse: (response) => {
|
|
3702
|
-
supported =
|
|
3703
|
-
response.headers.get('X-Deepline-Idempotency-Supported') === 'true';
|
|
3704
|
-
},
|
|
3705
|
-
},
|
|
3706
|
-
);
|
|
3707
|
-
if (!supported) {
|
|
3708
|
-
throw new DeeplineError(
|
|
3709
|
-
'This Deepline server does not support recoverable tool executions; no tool was dispatched.',
|
|
3710
|
-
422,
|
|
3711
|
-
'IDEMPOTENCY_NOT_SUPPORTED',
|
|
3712
|
-
);
|
|
3713
|
-
}
|
|
3714
|
-
return result;
|
|
3207
|
+
return this.toolCalls.getByKey(idempotencyKey);
|
|
3715
3208
|
}
|
|
3716
3209
|
|
|
3717
3210
|
/** Public recovery namespace. */
|
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.233',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|