deepline 0.1.180 → 0.1.182
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/apps/play-runner-workers/src/entry.ts +241 -4
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/output-datasets.ts +124 -12
- package/dist/bundling-sources/sdk/src/client.ts +7 -5
- package/dist/bundling-sources/sdk/src/play.ts +200 -26
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +263 -44
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/query-result-dataset.ts +120 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +139 -17
- package/dist/cli/index.js +12 -9
- package/dist/cli/index.mjs +12 -9
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +213 -28
- package/dist/index.mjs +213 -28
- package/package.json +1 -1
|
@@ -41,6 +41,7 @@ import { InMemoryRateStateBackend } from './governor/in-memory-rate-state-backen
|
|
|
41
41
|
import { pacingPolicyForTool } from './pacing';
|
|
42
42
|
import {
|
|
43
43
|
cloneToolExecuteResultWithExecution,
|
|
44
|
+
attachToolResultListDataset,
|
|
44
45
|
createToolExecuteResult,
|
|
45
46
|
parseToolExecuteResponse,
|
|
46
47
|
isToolExecuteResult,
|
|
@@ -69,6 +70,12 @@ import {
|
|
|
69
70
|
buildDurableToolCallAuthScopeDigest,
|
|
70
71
|
buildDurableToolCallCacheKey,
|
|
71
72
|
} from './durable-call-cache';
|
|
73
|
+
import {
|
|
74
|
+
QUERY_RESULT_DATASET_PAGE_SIZE,
|
|
75
|
+
isCustomerDbDatasetTool,
|
|
76
|
+
isQueryResultDatasetReadRequest,
|
|
77
|
+
isQueryResultDatasetTool,
|
|
78
|
+
} from './query-result-dataset';
|
|
72
79
|
import {
|
|
73
80
|
RuntimeReceiptWaitTimeoutError,
|
|
74
81
|
executeWithDurableRuntimeReceipt,
|
|
@@ -266,7 +273,44 @@ function publicToolResponseEnvelope(value: unknown): {
|
|
|
266
273
|
|
|
267
274
|
const EXECUTE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
|
|
268
275
|
const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
|
|
276
|
+
const EXECUTE_RESPONSE_INTENT_HEADER = 'x-deepline-execute-response-intent';
|
|
269
277
|
const V2_EXECUTE_RESPONSE_CONTRACT = 'v2-tool-response';
|
|
278
|
+
function recordOrNull(value: unknown): Record<string, unknown> | null {
|
|
279
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
280
|
+
? (value as Record<string, unknown>)
|
|
281
|
+
: null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function rowsFromUnknown(value: unknown): Record<string, unknown>[] {
|
|
285
|
+
if (!Array.isArray(value)) return [];
|
|
286
|
+
return value.map((row) =>
|
|
287
|
+
row && typeof row === 'object' && !Array.isArray(row)
|
|
288
|
+
? (row as Record<string, unknown>)
|
|
289
|
+
: { value: row },
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function finiteNonNegativeInteger(value: unknown): number | null {
|
|
294
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return Math.floor(value);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function finitePositiveInteger(value: unknown): number | null {
|
|
301
|
+
const integer = finiteNonNegativeInteger(value);
|
|
302
|
+
return integer !== null && integer > 0 ? integer : null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
type ToolExecutionApiOptions = {
|
|
306
|
+
timeoutMs?: number;
|
|
307
|
+
customerDbDataset?: {
|
|
308
|
+
limit: number;
|
|
309
|
+
offset: number;
|
|
310
|
+
pageSize: number;
|
|
311
|
+
totalRows: number;
|
|
312
|
+
};
|
|
313
|
+
};
|
|
270
314
|
const IN_MEMORY_STEP_RESULT_PREVIEW_LIMIT = 25;
|
|
271
315
|
const BATCH_SIZE_LOG_SAMPLE_LIMIT = 10;
|
|
272
316
|
const STEP_PROGRAM_MAP_DEFINITION = Symbol('deepline.stepProgramMapDefinition');
|
|
@@ -1695,24 +1739,146 @@ export class PlayContextImpl {
|
|
|
1695
1739
|
metadata?: ToolResultMetadataInput | null;
|
|
1696
1740
|
meta?: Record<string, unknown>;
|
|
1697
1741
|
execution: ToolResultExecutionMetadata;
|
|
1742
|
+
requestInput?: Record<string, unknown>;
|
|
1698
1743
|
}): Promise<ToolExecuteResult> {
|
|
1699
1744
|
if (isToolExecuteResult(input.result)) {
|
|
1700
|
-
return
|
|
1745
|
+
return this.attachCustomerDbDatasetResult(
|
|
1746
|
+
input.toolId,
|
|
1747
|
+
input.requestInput,
|
|
1748
|
+
cloneToolExecuteResultWithExecution(input.result, input.execution),
|
|
1749
|
+
);
|
|
1701
1750
|
}
|
|
1702
1751
|
const publicToolResult = publicToolResponseEnvelope(input.result);
|
|
1703
|
-
return
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1752
|
+
return this.attachCustomerDbDatasetResult(
|
|
1753
|
+
input.toolId,
|
|
1754
|
+
input.requestInput,
|
|
1755
|
+
createToolExecuteResult({
|
|
1756
|
+
status: publicToolResult?.status ?? input.status,
|
|
1757
|
+
jobId: input.jobId,
|
|
1758
|
+
result: publicToolResult
|
|
1759
|
+
? {
|
|
1760
|
+
data: publicToolResult.raw,
|
|
1761
|
+
...(publicToolResult.meta ? { meta: publicToolResult.meta } : {}),
|
|
1762
|
+
}
|
|
1763
|
+
: input.result,
|
|
1764
|
+
metadata:
|
|
1765
|
+
input.metadata ??
|
|
1766
|
+
(await this.resolveToolResultMetadata(input.toolId)),
|
|
1767
|
+
execution: input.execution,
|
|
1768
|
+
meta: input.meta,
|
|
1769
|
+
}),
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
private attachCustomerDbDatasetResult(
|
|
1774
|
+
toolId: string,
|
|
1775
|
+
requestInput: Record<string, unknown> | undefined,
|
|
1776
|
+
wrapped: ToolExecuteResult,
|
|
1777
|
+
): ToolExecuteResult {
|
|
1778
|
+
if (!isQueryResultDatasetTool(toolId)) return wrapped;
|
|
1779
|
+
const raw = recordOrNull(wrapped.toolResponse.raw);
|
|
1780
|
+
const dataset = recordOrNull(raw?.dataset);
|
|
1781
|
+
const rows = rowsFromUnknown(raw?.rows);
|
|
1782
|
+
const totalRows = finiteNonNegativeInteger(dataset?.total_rows);
|
|
1783
|
+
const sql =
|
|
1784
|
+
typeof requestInput?.sql === 'string'
|
|
1785
|
+
? requestInput.sql
|
|
1786
|
+
: typeof requestInput?.query === 'string'
|
|
1787
|
+
? requestInput.query
|
|
1788
|
+
: typeof raw?.sql === 'string'
|
|
1789
|
+
? raw.sql
|
|
1790
|
+
: null;
|
|
1791
|
+
if (!dataset || totalRows === null || !sql) {
|
|
1792
|
+
return wrapped;
|
|
1793
|
+
}
|
|
1794
|
+
const originalRequestInput = requestInput as Record<string, unknown>;
|
|
1795
|
+
const datasetLimit =
|
|
1796
|
+
finitePositiveInteger(dataset.returned_limit) ?? totalRows;
|
|
1797
|
+
const previewRows = rows.slice(0, Math.min(rows.length, 25));
|
|
1798
|
+
const executionNonce =
|
|
1799
|
+
typeof wrapped.job_id === 'string' && wrapped.job_id.trim()
|
|
1800
|
+
? wrapped.job_id.trim()
|
|
1801
|
+
: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
1802
|
+
const datasetId = `tool-list:${sha256Hex(
|
|
1803
|
+
`${toolId}:${this.currentRunId}:${sql}:${datasetLimit}:${executionNonce}`,
|
|
1804
|
+
)}`;
|
|
1805
|
+
const fetchPage = async (
|
|
1806
|
+
offset: number,
|
|
1807
|
+
limit: number,
|
|
1808
|
+
): Promise<Record<string, unknown>[]> => {
|
|
1809
|
+
if (limit <= 0 || offset >= totalRows) return [];
|
|
1810
|
+
const execution = await this.callToolExecutionAPI(
|
|
1811
|
+
toolId,
|
|
1812
|
+
originalRequestInput,
|
|
1813
|
+
{
|
|
1814
|
+
timeoutMs: resolveToolRuntimeTimeoutMs(toolId),
|
|
1815
|
+
customerDbDataset: {
|
|
1816
|
+
limit: datasetLimit,
|
|
1817
|
+
offset,
|
|
1818
|
+
pageSize: Math.min(limit, QUERY_RESULT_DATASET_PAGE_SIZE),
|
|
1819
|
+
totalRows,
|
|
1820
|
+
},
|
|
1821
|
+
},
|
|
1822
|
+
);
|
|
1823
|
+
const pageRaw =
|
|
1824
|
+
recordOrNull(execution.toolResponse)?.raw ??
|
|
1825
|
+
recordOrNull(execution.result)?.data ??
|
|
1826
|
+
execution.result;
|
|
1827
|
+
return rowsFromUnknown(recordOrNull(pageRaw)?.rows);
|
|
1828
|
+
};
|
|
1829
|
+
const collectRows = async (
|
|
1830
|
+
limit: number | undefined,
|
|
1831
|
+
): Promise<Record<string, unknown>[]> => {
|
|
1832
|
+
const target = Math.min(limit ?? totalRows, totalRows, datasetLimit);
|
|
1833
|
+
const collected: Record<string, unknown>[] = [];
|
|
1834
|
+
for (
|
|
1835
|
+
let offset = 0;
|
|
1836
|
+
offset < target;
|
|
1837
|
+
offset += QUERY_RESULT_DATASET_PAGE_SIZE
|
|
1838
|
+
) {
|
|
1839
|
+
collected.push(
|
|
1840
|
+
...(await fetchPage(
|
|
1841
|
+
offset,
|
|
1842
|
+
Math.min(QUERY_RESULT_DATASET_PAGE_SIZE, target - offset),
|
|
1843
|
+
)),
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
return collected.slice(0, target);
|
|
1847
|
+
};
|
|
1848
|
+
const playDataset = createDeferredPlayDataset({
|
|
1849
|
+
datasetKind: 'csv',
|
|
1850
|
+
datasetId,
|
|
1851
|
+
count: Math.min(totalRows, datasetLimit),
|
|
1852
|
+
previewRows,
|
|
1853
|
+
sourceLabel: 'query result rows',
|
|
1854
|
+
tableNamespace: null,
|
|
1855
|
+
resolvers: {
|
|
1856
|
+
count: async () => Math.min(totalRows, datasetLimit),
|
|
1857
|
+
peek: async (limit) => collectRows(limit),
|
|
1858
|
+
materialize: async (limit) => collectRows(limit),
|
|
1859
|
+
iterate: () =>
|
|
1860
|
+
({
|
|
1861
|
+
async *[Symbol.asyncIterator]() {
|
|
1862
|
+
const count = Math.min(totalRows, datasetLimit);
|
|
1863
|
+
for (
|
|
1864
|
+
let offset = 0;
|
|
1865
|
+
offset < count;
|
|
1866
|
+
offset += QUERY_RESULT_DATASET_PAGE_SIZE
|
|
1867
|
+
) {
|
|
1868
|
+
yield* await fetchPage(
|
|
1869
|
+
offset,
|
|
1870
|
+
Math.min(QUERY_RESULT_DATASET_PAGE_SIZE, count - offset),
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
},
|
|
1874
|
+
}) as AsyncIterable<Record<string, unknown>>,
|
|
1875
|
+
},
|
|
1876
|
+
});
|
|
1877
|
+
return attachToolResultListDataset(wrapped, {
|
|
1878
|
+
name: 'rows',
|
|
1879
|
+
path: 'toolResponse.raw.rows',
|
|
1880
|
+
dataset: playDataset,
|
|
1881
|
+
count: Math.min(totalRows, datasetLimit),
|
|
1716
1882
|
});
|
|
1717
1883
|
}
|
|
1718
1884
|
|
|
@@ -1738,6 +1904,7 @@ export class PlayContextImpl {
|
|
|
1738
1904
|
cacheKey,
|
|
1739
1905
|
receiptKey,
|
|
1740
1906
|
}),
|
|
1907
|
+
requestInput: request.input,
|
|
1741
1908
|
});
|
|
1742
1909
|
let completed: RuntimeStepReceipt | null | undefined = null;
|
|
1743
1910
|
if (receiptKey) {
|
|
@@ -1791,6 +1958,7 @@ export class PlayContextImpl {
|
|
|
1791
1958
|
cacheKey: entry.request.cacheKey,
|
|
1792
1959
|
receiptKey: entry.request.receiptKey,
|
|
1793
1960
|
}),
|
|
1961
|
+
requestInput: entry.request.input,
|
|
1794
1962
|
}),
|
|
1795
1963
|
})),
|
|
1796
1964
|
);
|
|
@@ -1851,6 +2019,7 @@ export class PlayContextImpl {
|
|
|
1851
2019
|
? 'no_result'
|
|
1852
2020
|
: 'completed',
|
|
1853
2021
|
result: this.runtimeReceiptOutput(completed),
|
|
2022
|
+
requestInput: request.input,
|
|
1854
2023
|
execution: toolExecutionMetadataForOutcome(
|
|
1855
2024
|
completed.runId === this.currentRunId
|
|
1856
2025
|
? {
|
|
@@ -1867,7 +2036,9 @@ export class PlayContextImpl {
|
|
|
1867
2036
|
),
|
|
1868
2037
|
})
|
|
1869
2038
|
: wrapped;
|
|
1870
|
-
|
|
2039
|
+
if (request.cacheable !== false) {
|
|
2040
|
+
this.cacheToolResult(toolId, cacheKey, finalWrapped);
|
|
2041
|
+
}
|
|
1871
2042
|
|
|
1872
2043
|
const resolver = this.toolCallResolvers.get(request.callId);
|
|
1873
2044
|
if (resolver) {
|
|
@@ -2211,8 +2382,7 @@ export class PlayContextImpl {
|
|
|
2211
2382
|
playId: this.#options.playId,
|
|
2212
2383
|
runId: this.#options.runId,
|
|
2213
2384
|
staticPipeline: this.#options.staticPipeline,
|
|
2214
|
-
forceRefresh:
|
|
2215
|
-
this.#options.cachePolicy?.forceToolRefresh === true,
|
|
2385
|
+
forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
|
|
2216
2386
|
},
|
|
2217
2387
|
);
|
|
2218
2388
|
resolvedTableNamespace = normalizeTableNamespace(
|
|
@@ -3486,11 +3656,13 @@ export class PlayContextImpl {
|
|
|
3486
3656
|
toolId,
|
|
3487
3657
|
requestInput: input,
|
|
3488
3658
|
});
|
|
3659
|
+
const cacheableToolResult = !isQueryResultDatasetReadRequest(toolId, input);
|
|
3489
3660
|
const durableCacheKey = await this.durableToolCallCacheKey({
|
|
3490
3661
|
toolId,
|
|
3491
3662
|
requestInput: input,
|
|
3492
3663
|
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
3493
3664
|
});
|
|
3665
|
+
const durableReceiptKey = cacheableToolResult ? durableCacheKey : null;
|
|
3494
3666
|
const eventWaitHandler =
|
|
3495
3667
|
(await this.#options.getIntegrationEventWaitHandler?.(toolId)) ?? null;
|
|
3496
3668
|
const store = rowContext.getStore();
|
|
@@ -3518,15 +3690,18 @@ export class PlayContextImpl {
|
|
|
3518
3690
|
|
|
3519
3691
|
if (!store) {
|
|
3520
3692
|
const directCacheKey = durableCacheKey;
|
|
3521
|
-
const cached =
|
|
3693
|
+
const cached = !cacheableToolResult
|
|
3522
3694
|
? null
|
|
3523
|
-
:
|
|
3695
|
+
: toolCachePolicy.force
|
|
3696
|
+
? null
|
|
3697
|
+
: this.getCachedToolResult(toolId, directCacheKey);
|
|
3524
3698
|
if (cached?.done) {
|
|
3525
3699
|
this.log(`Tool cache hit: ${toolId} exact payload`);
|
|
3526
3700
|
return await this.wrapToolExecutionResult({
|
|
3527
3701
|
toolId,
|
|
3528
3702
|
status: cached.result == null ? 'no_result' : 'completed',
|
|
3529
3703
|
result: cached.result,
|
|
3704
|
+
requestInput: input,
|
|
3530
3705
|
execution: toolExecutionMetadataForOutcome({
|
|
3531
3706
|
kind: 'checkpoint',
|
|
3532
3707
|
cacheKey: directCacheKey,
|
|
@@ -3548,19 +3723,22 @@ export class PlayContextImpl {
|
|
|
3548
3723
|
result: execution.result,
|
|
3549
3724
|
metadata: execution.metadata,
|
|
3550
3725
|
meta: execution.meta,
|
|
3726
|
+
requestInput: input,
|
|
3551
3727
|
execution: toolExecutionMetadataForOutcome({
|
|
3552
3728
|
kind: 'live',
|
|
3553
3729
|
cacheKey: directCacheKey,
|
|
3554
3730
|
}),
|
|
3555
3731
|
});
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3732
|
+
if (cacheableToolResult) {
|
|
3733
|
+
this.checkpoint.completedToolBatches[toolId] = {
|
|
3734
|
+
...(this.checkpoint.completedToolBatches[toolId] ?? {}),
|
|
3735
|
+
[directCacheKey]: {
|
|
3736
|
+
done: true,
|
|
3737
|
+
result: wrapped,
|
|
3738
|
+
},
|
|
3739
|
+
};
|
|
3740
|
+
this.#options.onBatchComplete?.(this.checkpoint);
|
|
3741
|
+
}
|
|
3564
3742
|
return wrapped;
|
|
3565
3743
|
}
|
|
3566
3744
|
|
|
@@ -3581,15 +3759,18 @@ export class PlayContextImpl {
|
|
|
3581
3759
|
}
|
|
3582
3760
|
|
|
3583
3761
|
const toolResultCacheKey = durableCacheKey;
|
|
3584
|
-
const cached =
|
|
3762
|
+
const cached = !cacheableToolResult
|
|
3585
3763
|
? null
|
|
3586
|
-
:
|
|
3764
|
+
: toolCachePolicy.force
|
|
3765
|
+
? null
|
|
3766
|
+
: this.getCachedToolResult(toolId, toolResultCacheKey);
|
|
3587
3767
|
if (cached?.done) {
|
|
3588
3768
|
this.log(` Row ${rowId} ${toolId}: cache hit exact payload`);
|
|
3589
3769
|
return await this.wrapToolExecutionResult({
|
|
3590
3770
|
toolId,
|
|
3591
3771
|
status: cached.result == null ? 'no_result' : 'completed',
|
|
3592
3772
|
result: cached.result,
|
|
3773
|
+
requestInput: input,
|
|
3593
3774
|
execution: toolExecutionMetadataForOutcome({
|
|
3594
3775
|
kind: 'checkpoint',
|
|
3595
3776
|
cacheKey: toolResultCacheKey,
|
|
@@ -3624,7 +3805,8 @@ export class PlayContextImpl {
|
|
|
3624
3805
|
this.toolCallQueue.push({
|
|
3625
3806
|
callId,
|
|
3626
3807
|
cacheKey: toolResultCacheKey,
|
|
3627
|
-
|
|
3808
|
+
cacheable: cacheableToolResult,
|
|
3809
|
+
receiptKey: durableReceiptKey,
|
|
3628
3810
|
force: toolCachePolicy.force,
|
|
3629
3811
|
rowId,
|
|
3630
3812
|
fieldName,
|
|
@@ -3641,7 +3823,7 @@ export class PlayContextImpl {
|
|
|
3641
3823
|
});
|
|
3642
3824
|
};
|
|
3643
3825
|
|
|
3644
|
-
if (store) {
|
|
3826
|
+
if (store || !cacheableToolResult) {
|
|
3645
3827
|
return await executeTool();
|
|
3646
3828
|
}
|
|
3647
3829
|
|
|
@@ -3650,7 +3832,7 @@ export class PlayContextImpl {
|
|
|
3650
3832
|
normalizedKey,
|
|
3651
3833
|
this.currentRunId,
|
|
3652
3834
|
{
|
|
3653
|
-
receiptKey:
|
|
3835
|
+
receiptKey: durableReceiptKey,
|
|
3654
3836
|
semanticKey: toolRequestIdentity,
|
|
3655
3837
|
force: toolCachePolicy.force,
|
|
3656
3838
|
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
@@ -4347,17 +4529,21 @@ export class PlayContextImpl {
|
|
|
4347
4529
|
await Promise.all(
|
|
4348
4530
|
[...byTool.entries()].map(async ([toolId, requests]) => {
|
|
4349
4531
|
this.log(`Executing tool batch ${toolId}: ${requests.length} calls`);
|
|
4532
|
+
const liveStepResults = new Map<string, unknown>();
|
|
4350
4533
|
|
|
4351
4534
|
const recordToolStep = (stepRequests: ToolCallRequest[]): void => {
|
|
4352
4535
|
if (stepRequests.length === 0) return;
|
|
4353
4536
|
const stepResults: PlayStepRowResult[] = stepRequests.map((req) => {
|
|
4354
|
-
const
|
|
4537
|
+
const hasLiveResult = liveStepResults.has(req.callId);
|
|
4538
|
+
const result = hasLiveResult
|
|
4539
|
+
? liveStepResults.get(req.callId)
|
|
4540
|
+
: this.getCachedToolResult(toolId, req.cacheKey)?.result;
|
|
4355
4541
|
return {
|
|
4356
4542
|
rowId: req.rowId,
|
|
4357
|
-
status:
|
|
4358
|
-
success:
|
|
4359
|
-
value:
|
|
4360
|
-
error:
|
|
4543
|
+
status: result != null ? 'completed' : 'failed',
|
|
4544
|
+
success: result != null,
|
|
4545
|
+
value: result,
|
|
4546
|
+
error: result != null ? null : 'Tool call failed',
|
|
4361
4547
|
};
|
|
4362
4548
|
});
|
|
4363
4549
|
const toolStep = {
|
|
@@ -4377,9 +4563,12 @@ export class PlayContextImpl {
|
|
|
4377
4563
|
const pendingRequests: ToolCallRequest[] = [];
|
|
4378
4564
|
const recoveredRequests: ToolCallRequest[] = [];
|
|
4379
4565
|
for (const req of requests) {
|
|
4380
|
-
const cached =
|
|
4381
|
-
|
|
4382
|
-
|
|
4566
|
+
const cached =
|
|
4567
|
+
req.cacheable === false
|
|
4568
|
+
? undefined
|
|
4569
|
+
: req.force
|
|
4570
|
+
? undefined
|
|
4571
|
+
: this.getCachedToolResult(toolId, req.cacheKey);
|
|
4383
4572
|
if (cached?.done) {
|
|
4384
4573
|
this.log(` Row ${req.rowId} ${toolId}: recovered from checkpoint`);
|
|
4385
4574
|
const resolver = this.toolCallResolvers.get(req.callId);
|
|
@@ -4411,7 +4600,9 @@ export class PlayContextImpl {
|
|
|
4411
4600
|
attachedToReceiptKey: followerReceiptKey,
|
|
4412
4601
|
})
|
|
4413
4602
|
: result;
|
|
4414
|
-
|
|
4603
|
+
if (follower.cacheable !== false) {
|
|
4604
|
+
this.cacheToolResult(toolId, follower.cacheKey, followerResult);
|
|
4605
|
+
}
|
|
4415
4606
|
const resolver = this.toolCallResolvers.get(follower.callId);
|
|
4416
4607
|
if (resolver) {
|
|
4417
4608
|
resolver.resolve(followerResult);
|
|
@@ -4558,6 +4749,7 @@ export class PlayContextImpl {
|
|
|
4558
4749
|
? 'no_result'
|
|
4559
4750
|
: 'completed',
|
|
4560
4751
|
result: this.runtimeReceiptOutput(receipt),
|
|
4752
|
+
requestInput: request.input,
|
|
4561
4753
|
execution: toolExecutionMetadataForOutcome({
|
|
4562
4754
|
kind: source,
|
|
4563
4755
|
cacheKey: request.cacheKey,
|
|
@@ -4689,6 +4881,7 @@ export class PlayContextImpl {
|
|
|
4689
4881
|
execution?.jobId,
|
|
4690
4882
|
execution?.meta,
|
|
4691
4883
|
);
|
|
4884
|
+
liveStepResults.set(owner.callId, result);
|
|
4692
4885
|
resolveLiveFollowers(owner, result);
|
|
4693
4886
|
recordToolStep([owner]);
|
|
4694
4887
|
this.#options.onBatchComplete?.(this.checkpoint);
|
|
@@ -4837,6 +5030,7 @@ export class PlayContextImpl {
|
|
|
4837
5030
|
index += 1
|
|
4838
5031
|
) {
|
|
4839
5032
|
const request = entry.request.memberRequests[index]!;
|
|
5033
|
+
liveStepResults.set(request.callId, resolvedResults[index]);
|
|
4840
5034
|
resolveLiveFollowers(request, resolvedResults[index]);
|
|
4841
5035
|
}
|
|
4842
5036
|
}
|
|
@@ -4894,6 +5088,7 @@ export class PlayContextImpl {
|
|
|
4894
5088
|
execution?.jobId,
|
|
4895
5089
|
execution?.meta,
|
|
4896
5090
|
);
|
|
5091
|
+
liveStepResults.set(request.callId, result);
|
|
4897
5092
|
resolveLiveFollowers(request, result);
|
|
4898
5093
|
} catch (error) {
|
|
4899
5094
|
await rejectWithLiveFollowers(request, error);
|
|
@@ -4989,7 +5184,7 @@ export class PlayContextImpl {
|
|
|
4989
5184
|
private async callToolAPI(
|
|
4990
5185
|
toolId: string,
|
|
4991
5186
|
input: Record<string, unknown>,
|
|
4992
|
-
options?:
|
|
5187
|
+
options?: ToolExecutionApiOptions,
|
|
4993
5188
|
): Promise<unknown> {
|
|
4994
5189
|
const execution = await this.callToolExecutionAPI(toolId, input, options);
|
|
4995
5190
|
if (execution.toolResponse && 'raw' in execution.toolResponse) {
|
|
@@ -5006,7 +5201,7 @@ export class PlayContextImpl {
|
|
|
5006
5201
|
private async callToolExecutionAPI(
|
|
5007
5202
|
toolId: string,
|
|
5008
5203
|
input: Record<string, unknown>,
|
|
5009
|
-
options?:
|
|
5204
|
+
options?: ToolExecutionApiOptions,
|
|
5010
5205
|
): Promise<ParsedToolExecuteResponse> {
|
|
5011
5206
|
if (!this.#options.executorToken || !this.#options.baseUrl) {
|
|
5012
5207
|
throw new Error(
|
|
@@ -5064,6 +5259,7 @@ export class PlayContextImpl {
|
|
|
5064
5259
|
Authorization: `Bearer ${this.#options.executorToken}`,
|
|
5065
5260
|
[EXECUTE_RESPONSE_CONTRACT_HEADER]:
|
|
5066
5261
|
V2_EXECUTE_RESPONSE_CONTRACT,
|
|
5262
|
+
[EXECUTE_RESPONSE_INTENT_HEADER]: 'dataset',
|
|
5067
5263
|
[EXECUTE_TOOL_METADATA_HEADER]: 'true',
|
|
5068
5264
|
...(this.#options.vercelProtectionBypassToken?.trim()
|
|
5069
5265
|
? {
|
|
@@ -5074,7 +5270,30 @@ export class PlayContextImpl {
|
|
|
5074
5270
|
},
|
|
5075
5271
|
body: JSON.stringify({
|
|
5076
5272
|
payload: input,
|
|
5077
|
-
metadata: {
|
|
5273
|
+
metadata: {
|
|
5274
|
+
parent_run_id: this.#options.runId,
|
|
5275
|
+
...(options?.customerDbDataset
|
|
5276
|
+
? {
|
|
5277
|
+
query_result_dataset: {
|
|
5278
|
+
limit: options.customerDbDataset.limit,
|
|
5279
|
+
offset: options.customerDbDataset.offset,
|
|
5280
|
+
page_size: options.customerDbDataset.pageSize,
|
|
5281
|
+
total_rows: options.customerDbDataset.totalRows,
|
|
5282
|
+
},
|
|
5283
|
+
...(isCustomerDbDatasetTool(toolId)
|
|
5284
|
+
? {
|
|
5285
|
+
customer_db_dataset: {
|
|
5286
|
+
limit: options.customerDbDataset.limit,
|
|
5287
|
+
offset: options.customerDbDataset.offset,
|
|
5288
|
+
page_size: options.customerDbDataset.pageSize,
|
|
5289
|
+
total_rows:
|
|
5290
|
+
options.customerDbDataset.totalRows,
|
|
5291
|
+
},
|
|
5292
|
+
}
|
|
5293
|
+
: {}),
|
|
5294
|
+
}
|
|
5295
|
+
: {}),
|
|
5296
|
+
},
|
|
5078
5297
|
...(this.#options.integrationMode
|
|
5079
5298
|
? { integration_mode: this.#options.integrationMode }
|
|
5080
5299
|
: {}),
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export type QueryResultDatasetRequest = {
|
|
2
|
+
limit?: number;
|
|
3
|
+
offset?: number;
|
|
4
|
+
pageSize?: number;
|
|
5
|
+
totalRows?: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const QUERY_RESULT_DATASET_PAGE_SIZE = 1_000;
|
|
9
|
+
|
|
10
|
+
function maskSqlForDatasetClassifier(sql: string): string {
|
|
11
|
+
let output = '';
|
|
12
|
+
let index = 0;
|
|
13
|
+
while (index < sql.length) {
|
|
14
|
+
const char = sql[index];
|
|
15
|
+
const next = sql[index + 1];
|
|
16
|
+
if (char === '-' && next === '-') {
|
|
17
|
+
const newline = sql.indexOf('\n', index + 2);
|
|
18
|
+
output += ' ';
|
|
19
|
+
index = newline === -1 ? sql.length : newline + 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (char === '/' && next === '*') {
|
|
23
|
+
index += 2;
|
|
24
|
+
let depth = 1;
|
|
25
|
+
while (index < sql.length && depth > 0) {
|
|
26
|
+
if (sql[index] === '/' && sql[index + 1] === '*') {
|
|
27
|
+
depth += 1;
|
|
28
|
+
index += 2;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (sql[index] === '*' && sql[index + 1] === '/') {
|
|
32
|
+
depth -= 1;
|
|
33
|
+
index += 2;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
index += 1;
|
|
37
|
+
}
|
|
38
|
+
output += ' ';
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (char === "'" || char === '"') {
|
|
42
|
+
const quote = char;
|
|
43
|
+
output += ' ';
|
|
44
|
+
index += 1;
|
|
45
|
+
while (index < sql.length) {
|
|
46
|
+
const quoted = sql[index];
|
|
47
|
+
if (quoted === quote && sql[index + 1] === quote) {
|
|
48
|
+
index += 2;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
index += 1;
|
|
52
|
+
if (quoted === quote) break;
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
output += char;
|
|
57
|
+
index += 1;
|
|
58
|
+
}
|
|
59
|
+
return output;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isCustomerDbDatasetTool(toolId: string): boolean {
|
|
63
|
+
return /^(?:customer_db_)?query_customer_db$/.test(toolId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function isSnowflakeQueryDatasetTool(toolId: string): boolean {
|
|
67
|
+
return /^snowflake_(?:snowflake_)?run_(?:semantic_)?query$/.test(toolId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isSnowflakeSemanticQueryDatasetTool(toolId: string): boolean {
|
|
71
|
+
return /^snowflake_(?:snowflake_)?run_semantic_query$/.test(toolId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function isQueryResultDatasetTool(toolId: string): boolean {
|
|
75
|
+
return isCustomerDbDatasetTool(toolId) || isSnowflakeQueryDatasetTool(toolId);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function isQueryResultDatasetOperation(input: {
|
|
79
|
+
provider?: string | null;
|
|
80
|
+
operation?: string | null;
|
|
81
|
+
}): boolean {
|
|
82
|
+
return (
|
|
83
|
+
(input.provider === 'customer_db' &&
|
|
84
|
+
input.operation === 'query_customer_db') ||
|
|
85
|
+
(input.provider === 'snowflake' &&
|
|
86
|
+
(input.operation === 'snowflake_run_query' ||
|
|
87
|
+
input.operation === 'snowflake_run_semantic_query'))
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isReadOnlySelectOrWith(sql: string): boolean {
|
|
92
|
+
const normalized = maskSqlForDatasetClassifier(sql).trimStart().toLowerCase();
|
|
93
|
+
return (
|
|
94
|
+
(normalized.startsWith('select') &&
|
|
95
|
+
!/\bselect\b[\s\S]*?\binto\b/i.test(normalized)) ||
|
|
96
|
+
(normalized.startsWith('with') &&
|
|
97
|
+
!/\b(insert\s+into|update|delete\s+from|merge\s+into|select\b[\s\S]*?\binto|create|alter|drop|truncate)\b/i.test(
|
|
98
|
+
normalized,
|
|
99
|
+
))
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isQueryResultDatasetReadRequest(
|
|
104
|
+
toolId: string,
|
|
105
|
+
input: Record<string, unknown>,
|
|
106
|
+
): boolean {
|
|
107
|
+
if (!isQueryResultDatasetTool(toolId)) return false;
|
|
108
|
+
if (isSnowflakeQueryDatasetTool(toolId) && input.write === true) return false;
|
|
109
|
+
if (isSnowflakeSemanticQueryDatasetTool(toolId)) return true;
|
|
110
|
+
const sql =
|
|
111
|
+
typeof input.sql === 'string'
|
|
112
|
+
? input.sql
|
|
113
|
+
: typeof input.query === 'string'
|
|
114
|
+
? input.query
|
|
115
|
+
: '';
|
|
116
|
+
return isReadOnlySelectOrWith(sql);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const isCustomerDbDatasetReadRequest =
|
|
120
|
+
isQueryResultDatasetReadRequest;
|