@purista/harness 1.2.6 → 1.5.1
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 +6 -0
- package/dist/agents/index.d.ts +7 -1
- package/dist/agents/index.js +126 -44
- package/dist/errors/catalog.d.ts +18 -2
- package/dist/errors/catalog.js +10 -0
- package/dist/eval/index.d.ts +3 -3
- package/dist/eval/index.js +15 -1
- package/dist/harness/defineHarness.d.ts +149 -3
- package/dist/harness/defineHarness.js +110 -1
- package/dist/index.d.ts +38 -18
- package/dist/index.js +30 -16
- package/dist/local/index.d.ts +36 -0
- package/dist/local/index.js +24 -0
- package/dist/local/local-sandbox.d.ts +25 -0
- package/dist/local/local-sandbox.js +368 -0
- package/dist/local/local-workspace.d.ts +56 -0
- package/dist/local/local-workspace.js +496 -0
- package/dist/local/ref-hash.d.ts +6 -0
- package/dist/local/ref-hash.js +9 -0
- package/dist/local/sqlite-storage.d.ts +106 -0
- package/dist/local/sqlite-storage.js +680 -0
- package/dist/models/adapter-utils.d.ts +52 -0
- package/dist/models/adapter-utils.js +81 -0
- package/dist/models/registry.js +28 -37
- package/dist/models/stream-pump.d.ts +16 -0
- package/dist/models/stream-pump.js +77 -0
- package/dist/ports/base-model-provider.d.ts +7 -1
- package/dist/ports/base-model-provider.js +384 -87
- package/dist/ports/capabilities.d.ts +16 -2
- package/dist/ports/context-checkpoints.d.ts +63 -0
- package/dist/ports/context-checkpoints.js +33 -0
- package/dist/ports/index.d.ts +1 -0
- package/dist/ports/index.js +1 -0
- package/dist/ports/model-provider.d.ts +94 -0
- package/dist/runtime/durable.d.ts +11 -0
- package/dist/runtime/durable.js +15 -2
- package/dist/runtime/sessionDurable.js +47 -21
- package/dist/runtime/steps.d.ts +22 -1
- package/dist/runtime/steps.js +53 -2
- package/dist/sessions/index.d.ts +17 -6
- package/dist/sessions/index.js +345 -84
- package/dist/skills/index.d.ts +0 -2
- package/dist/skills/index.js +0 -8
- package/dist/state/in-memory.js +6 -6
- package/dist/telemetry/shim.js +2 -6
- package/dist/telemetry/span-attrs.d.ts +9 -0
- package/dist/telemetry/span-attrs.js +27 -0
- package/dist/testing/durableWorkspaceStoreContract.js +69 -0
- package/dist/testing/fakeLogger.d.ts +29 -0
- package/dist/testing/fakeLogger.js +47 -0
- package/dist/testing/fakeSandbox.d.ts +27 -0
- package/dist/testing/fakeSandbox.js +153 -0
- package/dist/testing/fakeStateStore.d.ts +36 -0
- package/dist/testing/fakeStateStore.js +66 -0
- package/dist/testing/index.d.ts +10 -4
- package/dist/testing/index.js +14 -4
- package/dist/testing/loggerContract.d.ts +9 -0
- package/dist/testing/loggerContract.js +62 -0
- package/dist/testing/modelProviderContract.d.ts +12 -0
- package/dist/testing/modelProviderContract.js +222 -0
- package/dist/testing/recordEvents.d.ts +3 -0
- package/dist/testing/recordEvents.js +8 -0
- package/dist/testing/stateStoreContract.js +27 -0
- package/dist/tools/index.js +26 -1
- package/dist/tools/mcp/http.d.ts +2 -0
- package/dist/tools/mcp/http.js +34 -21
- package/dist/tools/mcp/runner.d.ts +4 -0
- package/dist/tools/mcp/runner.js +75 -21
- package/dist/tools/mcp/stdio.d.ts +7 -1
- package/dist/tools/mcp/stdio.js +102 -23
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace/in-memory.d.ts +1 -0
- package/dist/workspace/in-memory.js +47 -12
- package/package.json +5 -4
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ModelError } from '../errors/index.js';
|
|
2
|
+
import type { JsonValue } from './json.js';
|
|
3
|
+
import type { TokenUsage, ToolCallSpec } from '../ports/model-provider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared helpers for first-party model adapter packages.
|
|
6
|
+
*
|
|
7
|
+
* Adapters import these from `@purista/harness` so error shapes, token usage
|
|
8
|
+
* accounting, structured-JSON handling, and OpenAI-compatible stream tool-call
|
|
9
|
+
* accumulation stay identical across providers.
|
|
10
|
+
*/
|
|
11
|
+
/** Identifies the provider call an adapter helper acts on behalf of. */
|
|
12
|
+
export interface AdapterCallContext {
|
|
13
|
+
provider: string;
|
|
14
|
+
model: string;
|
|
15
|
+
method: string;
|
|
16
|
+
}
|
|
17
|
+
/** Builds normalized token usage from optional provider token counts. */
|
|
18
|
+
export declare function toTokenUsage(inputTokens?: number, outputTokens?: number, totalTokens?: number): TokenUsage;
|
|
19
|
+
/**
|
|
20
|
+
* Replaces raw provider/model content with a redaction-safe descriptor.
|
|
21
|
+
*
|
|
22
|
+
* Raw strings are model output (POR-07 forbids logging/tracing them), so they
|
|
23
|
+
* collapse into `{ redacted, contentLength }`. Structured values pass through
|
|
24
|
+
* the provider-body redaction rules.
|
|
25
|
+
*/
|
|
26
|
+
export declare function redactProviderContent(body: unknown): unknown;
|
|
27
|
+
/** Builds the shared `malformed_response` ModelError with a content-redacted provider body. */
|
|
28
|
+
export declare function malformedResponseError(ctx: AdapterCallContext, message: string, body: unknown, cause: unknown): ModelError;
|
|
29
|
+
/** Parses provider JSON output, throwing the shared malformed-response error on failure. */
|
|
30
|
+
export declare function parseProviderJson(content: string, ctx: AdapterCallContext, message: string): JsonValue;
|
|
31
|
+
/** Wraps not-yet-valid JSON fragments for partial object stream chunks. */
|
|
32
|
+
export declare function safePartialJson(content: string): JsonValue;
|
|
33
|
+
/** Drops the synthetic `harness_response` object tool from extracted tool calls. */
|
|
34
|
+
export declare function withoutObjectTool(calls: ToolCallSpec[] | undefined): ToolCallSpec[] | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Per-index accumulator for OpenAI-compatible streamed tool-call fragments.
|
|
37
|
+
*
|
|
38
|
+
* The first delta carries `index`/`id`/`function.name` with partial or empty
|
|
39
|
+
* arguments; later deltas carry only `index` and argument fragments. Fragments
|
|
40
|
+
* are concatenated by index and parsed once at stream end.
|
|
41
|
+
*/
|
|
42
|
+
export type StreamToolCallState = Map<number, {
|
|
43
|
+
id?: string;
|
|
44
|
+
name?: string;
|
|
45
|
+
args: string;
|
|
46
|
+
}>;
|
|
47
|
+
/** Creates an empty OpenAI-compatible stream tool-call accumulator. */
|
|
48
|
+
export declare function createStreamToolCallState(): StreamToolCallState;
|
|
49
|
+
/** Accumulates OpenAI-compatible `delta.tool_calls` fragments by index. */
|
|
50
|
+
export declare function accumulateStreamToolCallDeltas(state: StreamToolCallState, deltas: unknown[]): void;
|
|
51
|
+
/** Finalizes accumulated stream tool calls, parsing empty argument payloads as `{}`. */
|
|
52
|
+
export declare function finalizeStreamToolCalls(state: StreamToolCallState, ctx: AdapterCallContext, malformedMessage: string): ToolCallSpec[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ModelError, sanitizeProviderBody } from '../errors/index.js';
|
|
2
|
+
/** Builds normalized token usage from optional provider token counts. */
|
|
3
|
+
export function toTokenUsage(inputTokens, outputTokens, totalTokens) {
|
|
4
|
+
const input = inputTokens ?? 0;
|
|
5
|
+
const output = outputTokens ?? 0;
|
|
6
|
+
return { inputTokens: input, outputTokens: output, totalTokens: totalTokens ?? input + output };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Replaces raw provider/model content with a redaction-safe descriptor.
|
|
10
|
+
*
|
|
11
|
+
* Raw strings are model output (POR-07 forbids logging/tracing them), so they
|
|
12
|
+
* collapse into `{ redacted, contentLength }`. Structured values pass through
|
|
13
|
+
* the provider-body redaction rules.
|
|
14
|
+
*/
|
|
15
|
+
export function redactProviderContent(body) {
|
|
16
|
+
if (typeof body === 'string')
|
|
17
|
+
return { redacted: true, contentLength: body.length };
|
|
18
|
+
return sanitizeProviderBody(body);
|
|
19
|
+
}
|
|
20
|
+
/** Builds the shared `malformed_response` ModelError with a content-redacted provider body. */
|
|
21
|
+
export function malformedResponseError(ctx, message, body, cause) {
|
|
22
|
+
return new ModelError(message, {
|
|
23
|
+
provider: ctx.provider,
|
|
24
|
+
model: ctx.model,
|
|
25
|
+
method: ctx.method,
|
|
26
|
+
reason: 'malformed_response',
|
|
27
|
+
providerBody: redactProviderContent(body)
|
|
28
|
+
}, cause);
|
|
29
|
+
}
|
|
30
|
+
/** Parses provider JSON output, throwing the shared malformed-response error on failure. */
|
|
31
|
+
export function parseProviderJson(content, ctx, message) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(content);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw malformedResponseError(ctx, message, content, error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Wraps not-yet-valid JSON fragments for partial object stream chunks. */
|
|
40
|
+
export function safePartialJson(content) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(content);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { _partial: content };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Drops the synthetic `harness_response` object tool from extracted tool calls. */
|
|
49
|
+
export function withoutObjectTool(calls) {
|
|
50
|
+
const filtered = calls?.filter((call) => call.name !== 'harness_response');
|
|
51
|
+
return filtered && filtered.length > 0 ? filtered : undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Creates an empty OpenAI-compatible stream tool-call accumulator. */
|
|
54
|
+
export function createStreamToolCallState() {
|
|
55
|
+
return new Map();
|
|
56
|
+
}
|
|
57
|
+
/** Accumulates OpenAI-compatible `delta.tool_calls` fragments by index. */
|
|
58
|
+
export function accumulateStreamToolCallDeltas(state, deltas) {
|
|
59
|
+
for (const delta of deltas) {
|
|
60
|
+
const index = typeof delta?.index === 'number' ? delta.index : 0;
|
|
61
|
+
const existing = state.get(index) ?? { args: '' };
|
|
62
|
+
if (delta?.id)
|
|
63
|
+
existing.id = String(delta.id);
|
|
64
|
+
if (delta?.function?.name)
|
|
65
|
+
existing.name = String(delta.function.name);
|
|
66
|
+
if (typeof delta?.function?.arguments === 'string')
|
|
67
|
+
existing.args += delta.function.arguments;
|
|
68
|
+
state.set(index, existing);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Finalizes accumulated stream tool calls, parsing empty argument payloads as `{}`. */
|
|
72
|
+
export function finalizeStreamToolCalls(state, ctx, malformedMessage) {
|
|
73
|
+
return [...state.entries()]
|
|
74
|
+
.sort((a, b) => a[0] - b[0])
|
|
75
|
+
.filter(([, call]) => call.id && call.name)
|
|
76
|
+
.map(([, call]) => ({
|
|
77
|
+
id: call.id,
|
|
78
|
+
name: call.name,
|
|
79
|
+
arguments: parseProviderJson(call.args || '{}', ctx, malformedMessage)
|
|
80
|
+
}));
|
|
81
|
+
}
|
package/dist/models/registry.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ModelCapabilityError, ModelError } from '../errors/index.js';
|
|
2
2
|
import { ATTR_GEN_AI_REQUEST_MODEL, ATTR_GEN_AI_RESPONSE_FINISH_REASONS, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_TOKEN_TYPE, ATTR_GEN_AI_USAGE_INPUT_TOKENS, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_TOKEN_TYPE_VALUE_INPUT, GEN_AI_TOKEN_TYPE_VALUE_OUTPUT } from '@opentelemetry/semantic-conventions/incubating';
|
|
3
|
+
import { pumpStreamThroughSpan } from './stream-pump.js';
|
|
3
4
|
/**
|
|
4
5
|
* Creates per-alias model handles that enforce capability gates before provider invocation.
|
|
5
6
|
*
|
|
@@ -88,7 +89,7 @@ function createHandle(aliasKey, alias, options) {
|
|
|
88
89
|
model: alias.model,
|
|
89
90
|
input: req.input,
|
|
90
91
|
...(req.dimensions !== undefined ? { dimensions: req.dimensions } : {}),
|
|
91
|
-
...(req.call ? { call: req.call } : {}),
|
|
92
|
+
...(mergeCallOptions(alias, req.call) ? { call: mergeCallOptions(alias, req.call) } : {}),
|
|
92
93
|
signal,
|
|
93
94
|
traceparent: req.traceparent ?? options.telemetry?.currentTraceparent()
|
|
94
95
|
};
|
|
@@ -103,7 +104,7 @@ function createHandle(aliasKey, alias, options) {
|
|
|
103
104
|
query: req.query,
|
|
104
105
|
documents: req.documents,
|
|
105
106
|
...(req.topN !== undefined ? { topN: req.topN } : {}),
|
|
106
|
-
...(req.call ? { call: req.call } : {}),
|
|
107
|
+
...(mergeCallOptions(alias, req.call) ? { call: mergeCallOptions(alias, req.call) } : {}),
|
|
107
108
|
signal,
|
|
108
109
|
traceparent: req.traceparent ?? options.telemetry?.currentTraceparent()
|
|
109
110
|
};
|
|
@@ -157,7 +158,7 @@ function withModelStreamSpan(options, aliasKey, alias, method, ctx, fn) {
|
|
|
157
158
|
return fn();
|
|
158
159
|
const started = Date.now();
|
|
159
160
|
const attrs = modelSpanAttrs(options, aliasKey, alias, method, ctx);
|
|
160
|
-
return
|
|
161
|
+
return pumpStreamThroughSpan(options.telemetry, `chat ${alias.model}`, attrs, async function* (span) {
|
|
161
162
|
let lastUsage;
|
|
162
163
|
let lastFinishReason;
|
|
163
164
|
for await (const chunk of fn()) {
|
|
@@ -185,40 +186,6 @@ function withModelStreamSpan(options, aliasKey, alias, method, ctx, fn) {
|
|
|
185
186
|
options.telemetry?.recordHistogram('gen_ai.client.operation.duration', (Date.now() - started) / 1000, attrs);
|
|
186
187
|
});
|
|
187
188
|
}
|
|
188
|
-
async function* streamWithTelemetry(telemetry, name, attrs, iterate) {
|
|
189
|
-
const queue = [];
|
|
190
|
-
let done = false;
|
|
191
|
-
let failure;
|
|
192
|
-
let notify;
|
|
193
|
-
const wake = () => {
|
|
194
|
-
notify?.();
|
|
195
|
-
notify = undefined;
|
|
196
|
-
};
|
|
197
|
-
const producer = telemetry.span(name, attrs, async (span) => {
|
|
198
|
-
for await (const chunk of iterate(span)) {
|
|
199
|
-
queue.push(chunk);
|
|
200
|
-
wake();
|
|
201
|
-
}
|
|
202
|
-
}).catch((error) => {
|
|
203
|
-
failure = error;
|
|
204
|
-
}).finally(() => {
|
|
205
|
-
done = true;
|
|
206
|
-
wake();
|
|
207
|
-
});
|
|
208
|
-
while (!done || queue.length > 0) {
|
|
209
|
-
const next = queue.shift();
|
|
210
|
-
if (next !== undefined) {
|
|
211
|
-
yield next;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (failure)
|
|
215
|
-
throw failure;
|
|
216
|
-
await new Promise((resolve) => { notify = resolve; });
|
|
217
|
-
}
|
|
218
|
-
await producer;
|
|
219
|
-
if (failure)
|
|
220
|
-
throw failure;
|
|
221
|
-
}
|
|
222
189
|
async function withModelSpan(options, aliasKey, alias, method, ctx, fn) {
|
|
223
190
|
if (!options.telemetry)
|
|
224
191
|
return fn();
|
|
@@ -335,10 +302,33 @@ function methodMissing(alias, method) {
|
|
|
335
302
|
}
|
|
336
303
|
/** Merges alias defaults with per-call overrides. */
|
|
337
304
|
function mergeDefaults(alias, call) {
|
|
305
|
+
const retry = call?.retry ?? alias.defaults?.retry ?? alias.retry;
|
|
338
306
|
const merged = {
|
|
339
307
|
...(alias.defaults ?? {}),
|
|
340
308
|
...(call ?? {}),
|
|
309
|
+
...(retry !== undefined ? { retry } : {}),
|
|
310
|
+
providerOptions: {
|
|
311
|
+
...(alias.providerOptions ?? {}),
|
|
312
|
+
...(alias.defaults?.providerOptions ?? {}),
|
|
313
|
+
...(call?.providerOptions ?? {})
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const hasTopLevel = merged.temperature !== undefined
|
|
317
|
+
|| merged.maxTokens !== undefined
|
|
318
|
+
|| merged.topP !== undefined
|
|
319
|
+
|| merged.stopSequences !== undefined
|
|
320
|
+
|| merged.parallelToolCalls !== undefined
|
|
321
|
+
|| merged.retry !== undefined
|
|
322
|
+
|| Object.keys(merged.providerOptions ?? {}).length > 0;
|
|
323
|
+
return hasTopLevel ? merged : undefined;
|
|
324
|
+
}
|
|
325
|
+
function mergeCallOptions(alias, call) {
|
|
326
|
+
const retry = call?.retry ?? alias.defaults?.retry ?? alias.retry;
|
|
327
|
+
const merged = {
|
|
328
|
+
...(call ?? {}),
|
|
329
|
+
...(retry !== undefined ? { retry } : {}),
|
|
341
330
|
providerOptions: {
|
|
331
|
+
...(alias.providerOptions ?? {}),
|
|
342
332
|
...(alias.defaults?.providerOptions ?? {}),
|
|
343
333
|
...(call?.providerOptions ?? {})
|
|
344
334
|
}
|
|
@@ -348,6 +338,7 @@ function mergeDefaults(alias, call) {
|
|
|
348
338
|
|| merged.topP !== undefined
|
|
349
339
|
|| merged.stopSequences !== undefined
|
|
350
340
|
|| merged.parallelToolCalls !== undefined
|
|
341
|
+
|| merged.retry !== undefined
|
|
351
342
|
|| Object.keys(merged.providerOptions ?? {}).length > 0;
|
|
352
343
|
return hasTopLevel ? merged : undefined;
|
|
353
344
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Span } from '@opentelemetry/api';
|
|
2
|
+
import type { SpanAttrs, TelemetryShim } from '../telemetry/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Bridges a span-scoped producer stream to a plain consumer `AsyncIterable`.
|
|
5
|
+
*
|
|
6
|
+
* The telemetry span must stay open for the full lifetime of the stream, so a
|
|
7
|
+
* producer task pulls chunks inside `telemetry.span(...)` and hands them to the
|
|
8
|
+
* consumer generator. Two guarantees keep this safe for model streams:
|
|
9
|
+
*
|
|
10
|
+
* - Backpressure: after pushing a chunk the producer waits until the consumer
|
|
11
|
+
* has drained it, so a slow consumer never accumulates an unbounded queue.
|
|
12
|
+
* - Consumer abandonment: when the consumer stops early (break/return/throw),
|
|
13
|
+
* the producer is woken, stops pulling, and closes the underlying iterator
|
|
14
|
+
* so the provider stream is released instead of draining to completion.
|
|
15
|
+
*/
|
|
16
|
+
export declare function pumpStreamThroughSpan<T>(telemetry: TelemetryShim, name: string, attrs: SpanAttrs, iterate: (span: Span) => AsyncIterable<T>): AsyncIterable<T>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges a span-scoped producer stream to a plain consumer `AsyncIterable`.
|
|
3
|
+
*
|
|
4
|
+
* The telemetry span must stay open for the full lifetime of the stream, so a
|
|
5
|
+
* producer task pulls chunks inside `telemetry.span(...)` and hands them to the
|
|
6
|
+
* consumer generator. Two guarantees keep this safe for model streams:
|
|
7
|
+
*
|
|
8
|
+
* - Backpressure: after pushing a chunk the producer waits until the consumer
|
|
9
|
+
* has drained it, so a slow consumer never accumulates an unbounded queue.
|
|
10
|
+
* - Consumer abandonment: when the consumer stops early (break/return/throw),
|
|
11
|
+
* the producer is woken, stops pulling, and closes the underlying iterator
|
|
12
|
+
* so the provider stream is released instead of draining to completion.
|
|
13
|
+
*/
|
|
14
|
+
export function pumpStreamThroughSpan(telemetry, name, attrs, iterate) {
|
|
15
|
+
return (async function* () {
|
|
16
|
+
const queue = [];
|
|
17
|
+
let producerDone = false;
|
|
18
|
+
let producerFailure;
|
|
19
|
+
let consumerAbandoned = false;
|
|
20
|
+
let notifyConsumer;
|
|
21
|
+
let notifyProducer;
|
|
22
|
+
const wakeConsumer = () => {
|
|
23
|
+
notifyConsumer?.();
|
|
24
|
+
notifyConsumer = undefined;
|
|
25
|
+
};
|
|
26
|
+
const wakeProducer = () => {
|
|
27
|
+
notifyProducer?.();
|
|
28
|
+
notifyProducer = undefined;
|
|
29
|
+
};
|
|
30
|
+
const producer = telemetry.span(name, attrs, async (span) => {
|
|
31
|
+
const iterator = iterate(span)[Symbol.asyncIterator]();
|
|
32
|
+
try {
|
|
33
|
+
while (!consumerAbandoned) {
|
|
34
|
+
const next = await iterator.next();
|
|
35
|
+
if (next.done)
|
|
36
|
+
return;
|
|
37
|
+
queue.push(next.value);
|
|
38
|
+
wakeConsumer();
|
|
39
|
+
// Backpressure: wait for consumer demand before pulling the next chunk.
|
|
40
|
+
while (queue.length > 0 && !consumerAbandoned) {
|
|
41
|
+
await new Promise((resolve) => { notifyProducer = resolve; });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
await iterator.return?.();
|
|
47
|
+
}
|
|
48
|
+
}).catch((error) => {
|
|
49
|
+
// Failures after consumer abandonment have no consumer left to observe them.
|
|
50
|
+
if (!consumerAbandoned)
|
|
51
|
+
producerFailure = error;
|
|
52
|
+
}).finally(() => {
|
|
53
|
+
producerDone = true;
|
|
54
|
+
wakeConsumer();
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
while (true) {
|
|
58
|
+
if (queue.length > 0) {
|
|
59
|
+
const chunk = queue.shift();
|
|
60
|
+
wakeProducer();
|
|
61
|
+
yield chunk;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (producerDone)
|
|
65
|
+
break;
|
|
66
|
+
await new Promise((resolve) => { notifyConsumer = resolve; });
|
|
67
|
+
}
|
|
68
|
+
if (producerFailure)
|
|
69
|
+
throw producerFailure;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
consumerAbandoned = true;
|
|
73
|
+
wakeProducer();
|
|
74
|
+
await producer;
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
}
|
|
@@ -54,4 +54,10 @@ export declare abstract class BaseModelProvider implements ModelProvider {
|
|
|
54
54
|
private attrs;
|
|
55
55
|
private recordUsage;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Shared adapter helpers (`models/adapter-utils.js`) are part of the public
|
|
59
|
+
* model-provider surface so first-party adapter packages consume a single
|
|
60
|
+
* implementation. They are re-exported next to the adapter base class because
|
|
61
|
+
* the ports barrel is the public path for adapter authors.
|
|
62
|
+
*/
|
|
63
|
+
export * from '../models/adapter-utils.js';
|