@superdurable/connectors-sdk 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -2
- package/dist/{chunk-2NBM7P4T.js → chunk-32YJXBE4.js} +1 -1
- package/dist/chunk-466BPS3M.js +336 -0
- package/dist/{chunk-SOXYPIMO.js → chunk-BQGYGCOQ.js} +35 -73
- package/dist/chunk-IHG6ADQG.js +44 -0
- package/dist/chunk-NQQJS6OC.js +316 -0
- package/dist/{chunk-KD5DYYSU.js → chunk-QDMBAWXD.js} +1 -1
- package/dist/{chunk-2IDXFKMY.js → chunk-UWB627CI.js} +82 -11
- package/dist/connectormock/index.d.ts +3 -2
- package/dist/connectormock/index.js +5 -3
- package/dist/credentials-JqG97kJ5.d.ts +102 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +19 -291
- package/dist/oauthtoken/index.d.ts +36 -2
- package/dist/oauthtoken/index.js +344 -3
- package/dist/{operation-Ca8rfacf.d.ts → operation-CASLOg86.d.ts} +2 -1
- package/dist/projectconfig/index.d.ts +2 -1
- package/dist/projectconfig/index.js +5 -3
- package/dist/providerhttp/index.d.ts +21 -3
- package/dist/providerhttp/index.js +3 -1
- package/dist/query-CeUaW9xU.d.ts +42 -0
- package/dist/singledispatch/index.d.ts +2 -1
- package/dist/singledispatch/index.js +2 -1
- package/dist/textgen/index.d.ts +15 -0
- package/dist/textgen/index.js +628 -0
- package/dist/textgen/openaichat/index.d.ts +44 -0
- package/dist/textgen/openaichat/index.js +232 -0
- package/dist/textgen/textgentest/index.d.ts +106 -0
- package/dist/textgen/textgentest/index.js +537 -0
- package/dist/{credentials-vrvW0d4j.d.ts → types-D6De3sry.d.ts} +1 -100
- package/dist/wire_format-stEVruMs.d.ts +190 -0
- package/package.json +14 -2
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
import {
|
|
2
|
+
goToBranch,
|
|
3
|
+
newQueryStep
|
|
4
|
+
} from "../../chunk-NQQJS6OC.js";
|
|
5
|
+
import "../../chunk-32YJXBE4.js";
|
|
6
|
+
import {
|
|
7
|
+
createCall
|
|
8
|
+
} from "../../chunk-QDMBAWXD.js";
|
|
9
|
+
import "../../chunk-BQGYGCOQ.js";
|
|
10
|
+
import "../../chunk-IHG6ADQG.js";
|
|
11
|
+
|
|
12
|
+
// src/textgen/textgentest/fake_provider.ts
|
|
13
|
+
import { createServer } from "http";
|
|
14
|
+
var maxRecordedRequestBytes = 16 << 20;
|
|
15
|
+
var redactedCredential = "[REDACTED]";
|
|
16
|
+
var FakeProvider = class _FakeProvider {
|
|
17
|
+
#server;
|
|
18
|
+
#credentialHeader;
|
|
19
|
+
#credential;
|
|
20
|
+
#replies = [];
|
|
21
|
+
#requests = [];
|
|
22
|
+
constructor(credentialHeader, credential) {
|
|
23
|
+
this.#credentialHeader = credentialHeader;
|
|
24
|
+
this.#credential = credential;
|
|
25
|
+
this.#server = createServer(this.serveRequest.bind(this));
|
|
26
|
+
}
|
|
27
|
+
/** Start one fixture server; close it after the suite. */
|
|
28
|
+
static async start(credentialHeader, credential) {
|
|
29
|
+
if (!credentialHeader.name || !credential) throw new TypeError("textgentest fake provider needs a credential header and credential");
|
|
30
|
+
const provider = new _FakeProvider(credentialHeader, credential);
|
|
31
|
+
await new Promise((resolve) => provider.#server.listen(0, "127.0.0.1", resolve));
|
|
32
|
+
return provider;
|
|
33
|
+
}
|
|
34
|
+
/** Return the loopback base URL. */
|
|
35
|
+
baseURL() {
|
|
36
|
+
const address = this.#server.address();
|
|
37
|
+
if (!address || typeof address === "string") throw new Error("fake provider is closed");
|
|
38
|
+
return `http://127.0.0.1:${address.port}`;
|
|
39
|
+
}
|
|
40
|
+
/** Queue replies in request order. */
|
|
41
|
+
enqueueReplies(...replies) {
|
|
42
|
+
this.#replies.push(...replies);
|
|
43
|
+
}
|
|
44
|
+
/** Return a snapshot of sanitized requests. */
|
|
45
|
+
requests() {
|
|
46
|
+
return [...this.#requests];
|
|
47
|
+
}
|
|
48
|
+
/** Stop the fixture and all active connections. */
|
|
49
|
+
async close() {
|
|
50
|
+
this.#server.closeAllConnections();
|
|
51
|
+
await new Promise((resolve) => this.#server.close(() => resolve()));
|
|
52
|
+
}
|
|
53
|
+
async serveRequest(incoming, response) {
|
|
54
|
+
const chunks = [];
|
|
55
|
+
let byteCount = 0;
|
|
56
|
+
try {
|
|
57
|
+
for await (const chunk of incoming) {
|
|
58
|
+
const bytes = chunk;
|
|
59
|
+
byteCount += bytes.length;
|
|
60
|
+
if (byteCount > maxRecordedRequestBytes) {
|
|
61
|
+
response.writeHead(413);
|
|
62
|
+
response.end();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
chunks.push(bytes);
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const body = Buffer.concat(chunks);
|
|
71
|
+
this.#requests.push(this.recordRequest(incoming, body));
|
|
72
|
+
const reply = this.#replies.shift();
|
|
73
|
+
if (!reply) {
|
|
74
|
+
response.writeHead(500, { "Content-Type": "application/json" });
|
|
75
|
+
response.end('{"error":{"type":"textgentest_no_scripted_reply"}}');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!await this.waitForDelay(response, reply.delayMs ?? 0)) return;
|
|
79
|
+
if (reply.shouldDropConnection) {
|
|
80
|
+
response.destroy();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
response.writeHead(reply.statusCode ?? 200, reply.headers);
|
|
84
|
+
if (reply.body) response.write(reply.body);
|
|
85
|
+
for (const piece of reply.streamChunks ?? []) {
|
|
86
|
+
if (!await this.waitForDelay(response, piece.delayMs ?? 0)) return;
|
|
87
|
+
if (response.destroyed) return;
|
|
88
|
+
response.write(piece.data);
|
|
89
|
+
}
|
|
90
|
+
response.end();
|
|
91
|
+
}
|
|
92
|
+
recordRequest(incoming, body) {
|
|
93
|
+
const slotName = this.#credentialHeader.name.toLowerCase();
|
|
94
|
+
const expected = this.#credentialHeader.prefix + this.#credential;
|
|
95
|
+
const headers = {};
|
|
96
|
+
let hasCredentialOutsideSlot = (incoming.url ?? "").includes(this.#credential) || body.includes(this.#credential);
|
|
97
|
+
for (const [name, value] of Object.entries(incoming.headers)) {
|
|
98
|
+
const rendered = Array.isArray(value) ? value.join(",") : value ?? "";
|
|
99
|
+
if (name === slotName) continue;
|
|
100
|
+
if (rendered.includes(this.#credential)) hasCredentialOutsideSlot = true;
|
|
101
|
+
headers[name] = rendered.replaceAll(this.#credential, redactedCredential);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
method: incoming.method ?? "",
|
|
105
|
+
path: (incoming.url ?? "").replaceAll(this.#credential, redactedCredential),
|
|
106
|
+
headers,
|
|
107
|
+
body: Buffer.from(body.toString().replaceAll(this.#credential, redactedCredential)),
|
|
108
|
+
receivedAt: /* @__PURE__ */ new Date(),
|
|
109
|
+
hasCredentialInSlot: incoming.headers[slotName] === expected,
|
|
110
|
+
hasCredentialOutsideSlot
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async waitForDelay(response, delayMs) {
|
|
114
|
+
if (delayMs <= 0) return !response.destroyed;
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const timer = setTimeout(() => {
|
|
117
|
+
response.off("close", onClose);
|
|
118
|
+
resolve(!response.destroyed);
|
|
119
|
+
}, delayMs);
|
|
120
|
+
const onClose = () => {
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
resolve(false);
|
|
123
|
+
};
|
|
124
|
+
response.once("close", onClose);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// src/textgen/textgentest/exchange_suite.ts
|
|
130
|
+
import assert from "assert/strict";
|
|
131
|
+
var credentialCanary = "sk-textgentest-credential-canary";
|
|
132
|
+
var providerMessageCanary = "textgentest-private-provider-message";
|
|
133
|
+
var generatedText = "The textgentest answer.";
|
|
134
|
+
var partialText = "The partial textgentest";
|
|
135
|
+
var reportedUsage = { inputTokens: 11, cachedInputTokens: 3, outputTokens: 7, reasoningTokens: 2, totalTokens: 18 };
|
|
136
|
+
async function runTextGenerationExchangeSuite(suite) {
|
|
137
|
+
const dialect = suite?.dialect;
|
|
138
|
+
if (!dialect?.credentialHeader.name || !dialect.connectionModel || !dialect.alternateModel || !suite.newQuery) {
|
|
139
|
+
throw new TypeError("text generation exchange suite is incomplete");
|
|
140
|
+
}
|
|
141
|
+
const provider = await FakeProvider.start(dialect.credentialHeader, credentialCanary);
|
|
142
|
+
try {
|
|
143
|
+
const query = await suite.newQuery({ baseURL: provider.baseURL(), model: dialect.connectionModel, apiKey: credentialCanary, maxResponseBytes: 64 << 10 });
|
|
144
|
+
const completion = { text: generatedText, servedModel: "textgentest-served-model", responseId: "textgentest-response-1", usage: reportedUsage };
|
|
145
|
+
const basic = () => ({ messages: [{ role: "user", text: "Say hello to textgentest." }] });
|
|
146
|
+
const invoke = (request) => query.invoke(createCall({
|
|
147
|
+
flowId: "textgentest-flow",
|
|
148
|
+
stepExecutionId: "generate:1",
|
|
149
|
+
attempt: 1,
|
|
150
|
+
recordHeartbeat: async () => {
|
|
151
|
+
}
|
|
152
|
+
}, { provider: "textgentest", name: "default" }, query.definition().operation), request);
|
|
153
|
+
const assertSafeRequest = (request) => {
|
|
154
|
+
assert.equal(request.method, "POST");
|
|
155
|
+
assert.equal(request.hasCredentialInSlot, true);
|
|
156
|
+
assert.equal(request.hasCredentialOutsideSlot, false);
|
|
157
|
+
assert.equal(request.body.includes(credentialCanary), false);
|
|
158
|
+
};
|
|
159
|
+
provider.enqueueReplies(dialect.generatedReply(completion));
|
|
160
|
+
const generated = await invoke(basic());
|
|
161
|
+
assert.equal(generated.kind, "branch");
|
|
162
|
+
if (generated.kind !== "branch") throw new Error("generated reply retried");
|
|
163
|
+
assert.equal(generated.branch, "generated");
|
|
164
|
+
assert.equal(generated.value.text, generatedText);
|
|
165
|
+
assert.equal(generated.value.requestedModel, dialect.connectionModel);
|
|
166
|
+
assert.equal(generated.value.servedModel, completion.servedModel);
|
|
167
|
+
assert.equal(generated.value.responseId, completion.responseId);
|
|
168
|
+
assert.deepEqual(generated.value.usage, reportedUsage);
|
|
169
|
+
const firstRequest = provider.requests()[0];
|
|
170
|
+
assert.ok(firstRequest);
|
|
171
|
+
assertSafeRequest(firstRequest);
|
|
172
|
+
assert.equal(dialect.readRequestModel(firstRequest), dialect.connectionModel);
|
|
173
|
+
if (dialect.requestIDHeader) assert.ok(generated.receipt.providerRequestId);
|
|
174
|
+
for (const pointer of dialect.optionalRequestFieldPointers ?? []) assert.equal(resolveJSONPointer(firstRequest.body, pointer), void 0);
|
|
175
|
+
provider.enqueueReplies(dialect.generatedReply(completion));
|
|
176
|
+
const alternate = await invoke({ ...basic(), model: dialect.alternateModel });
|
|
177
|
+
assert.equal(alternate.kind, "branch");
|
|
178
|
+
if (alternate.kind === "branch") assert.equal(alternate.branch, "generated");
|
|
179
|
+
const secondRequest = provider.requests()[1];
|
|
180
|
+
assert.ok(secondRequest);
|
|
181
|
+
assertSafeRequest(secondRequest);
|
|
182
|
+
assert.equal(dialect.readRequestModel(secondRequest), dialect.alternateModel);
|
|
183
|
+
provider.enqueueReplies(dialect.truncatedReply({ ...completion, text: partialText }));
|
|
184
|
+
const truncated = await invoke(basic());
|
|
185
|
+
assert.equal(truncated.kind, "branch");
|
|
186
|
+
if (truncated.kind === "branch") {
|
|
187
|
+
assert.equal(truncated.branch, "truncated");
|
|
188
|
+
assert.equal(truncated.value.text, partialText);
|
|
189
|
+
}
|
|
190
|
+
provider.enqueueReplies(dialect.blockedReply(completion));
|
|
191
|
+
const blocked = await invoke(basic());
|
|
192
|
+
assert.equal(blocked.kind, "branch");
|
|
193
|
+
if (blocked.kind === "branch") {
|
|
194
|
+
assert.equal(blocked.branch, "blocked");
|
|
195
|
+
assert.equal(blocked.value.text, "");
|
|
196
|
+
}
|
|
197
|
+
for (const status of [429, 503]) {
|
|
198
|
+
provider.enqueueReplies(dialect.errorReply(status, providerMessageCanary));
|
|
199
|
+
const outcome = await invoke(basic());
|
|
200
|
+
assert.equal(outcome.kind, "retry");
|
|
201
|
+
if (outcome.kind === "retry") assert.equal(outcome.failure.kind, status === 429 ? "RATE_LIMIT" : "AVAILABILITY");
|
|
202
|
+
assert.equal(outcome.failure?.message.includes(providerMessageCanary), false);
|
|
203
|
+
}
|
|
204
|
+
provider.enqueueReplies(dialect.errorReply(401, providerMessageCanary));
|
|
205
|
+
const rejected = await invoke(basic());
|
|
206
|
+
assert.equal(rejected.kind, "branch");
|
|
207
|
+
if (rejected.kind === "branch") assert.equal(rejected.branch, "providerRejected");
|
|
208
|
+
provider.enqueueReplies(dialect.errorReply(403, providerMessageCanary));
|
|
209
|
+
const forbidden = await invoke(basic());
|
|
210
|
+
assert.equal(forbidden.kind, "branch");
|
|
211
|
+
if (forbidden.kind === "branch") {
|
|
212
|
+
assert.equal(forbidden.branch, "providerRejected");
|
|
213
|
+
assert.equal(forbidden.failure?.kind, "AUTHORIZATION");
|
|
214
|
+
}
|
|
215
|
+
provider.enqueueReplies(dialect.quotaExhaustedReply(providerMessageCanary));
|
|
216
|
+
const quota = await invoke(basic());
|
|
217
|
+
assert.equal(quota.kind, "branch");
|
|
218
|
+
if (quota.kind === "branch") {
|
|
219
|
+
assert.equal(quota.branch, "providerRejected");
|
|
220
|
+
assert.equal(quota.failure?.kind, "QUOTA_EXHAUSTED");
|
|
221
|
+
}
|
|
222
|
+
const retryWithSeconds = dialect.errorReply(429, providerMessageCanary);
|
|
223
|
+
provider.enqueueReplies({ ...retryWithSeconds, headers: { ...retryWithSeconds.headers, "Retry-After": "7" } });
|
|
224
|
+
const seconds = await invoke(basic());
|
|
225
|
+
assert.equal(seconds.kind, "retry");
|
|
226
|
+
if (seconds.kind === "retry") assert.equal(seconds.retryAfterMs, 7e3);
|
|
227
|
+
const retryWithDate = dialect.errorReply(429, providerMessageCanary);
|
|
228
|
+
provider.enqueueReplies({ ...retryWithDate, headers: {
|
|
229
|
+
...retryWithDate.headers,
|
|
230
|
+
"Retry-After": new Date(Date.now() + 3e4).toUTCString()
|
|
231
|
+
} });
|
|
232
|
+
const dated = await invoke(basic());
|
|
233
|
+
assert.equal(dated.kind, "retry");
|
|
234
|
+
if (dated.kind === "retry") {
|
|
235
|
+
assert.ok(dated.retryAfterMs >= 25e3);
|
|
236
|
+
assert.ok(dated.retryAfterMs <= 3e4);
|
|
237
|
+
}
|
|
238
|
+
for (const status of [500, 502, 504]) {
|
|
239
|
+
provider.enqueueReplies(dialect.errorReply(status, providerMessageCanary));
|
|
240
|
+
const failure = await invoke(basic());
|
|
241
|
+
assert.equal(failure.kind, "retry");
|
|
242
|
+
if (failure.kind === "retry") assert.equal(failure.failure.kind, "AVAILABILITY");
|
|
243
|
+
}
|
|
244
|
+
provider.enqueueReplies({
|
|
245
|
+
statusCode: 503,
|
|
246
|
+
headers: { "Content-Type": "text/html" },
|
|
247
|
+
body: `<html>${providerMessageCanary}</html>`
|
|
248
|
+
});
|
|
249
|
+
const htmlGateway = await invoke(basic());
|
|
250
|
+
assert.equal(htmlGateway.kind, "retry");
|
|
251
|
+
assert.equal(htmlGateway.failure?.message.includes(providerMessageCanary), false);
|
|
252
|
+
const requestCountBeforeRedirect = provider.requests().length;
|
|
253
|
+
provider.enqueueReplies({ statusCode: 307, headers: { Location: `${provider.baseURL()}/redirected` } });
|
|
254
|
+
const redirect = await invoke(basic());
|
|
255
|
+
assert.equal(redirect.kind, "branch");
|
|
256
|
+
if (redirect.kind === "branch") assert.equal(redirect.branch, "invalidResponse");
|
|
257
|
+
assert.equal(provider.requests().length, requestCountBeforeRedirect + 1);
|
|
258
|
+
provider.enqueueReplies({ shouldDropConnection: true });
|
|
259
|
+
const dropped = await invoke(basic());
|
|
260
|
+
assert.equal(dropped.kind, "retry");
|
|
261
|
+
if (dropped.kind === "retry") assert.equal(dropped.failure.kind, "TRANSPORT");
|
|
262
|
+
provider.enqueueReplies(dialect.malformedReply());
|
|
263
|
+
const malformed = await invoke(basic());
|
|
264
|
+
assert.equal(malformed.kind, "branch");
|
|
265
|
+
if (malformed.kind === "branch") assert.equal(malformed.branch, "invalidResponse");
|
|
266
|
+
provider.enqueueReplies(dialect.generatedReply({ ...completion, text: "x".repeat(64 << 10) }));
|
|
267
|
+
const oversized = await invoke(basic());
|
|
268
|
+
assert.equal(oversized.kind, "branch");
|
|
269
|
+
if (oversized.kind === "branch") {
|
|
270
|
+
assert.equal(oversized.branch, "invalidResponse");
|
|
271
|
+
assert.equal(oversized.failure?.kind, "RESPONSE_TOO_LARGE");
|
|
272
|
+
}
|
|
273
|
+
if (query.requestFeatures().supportsStructuredOutput) {
|
|
274
|
+
const schema = { type: "object", additionalProperties: false, required: ["answer", "score"], properties: {
|
|
275
|
+
answer: { type: "string" },
|
|
276
|
+
score: { type: "integer", maximum: 10 }
|
|
277
|
+
} };
|
|
278
|
+
const structuredRequest = (selectedSchema) => ({
|
|
279
|
+
...basic(),
|
|
280
|
+
structuredOutput: { name: "textgentest_answer", schema: selectedSchema }
|
|
281
|
+
});
|
|
282
|
+
const matching = '{"answer":"forty-two","score":7}';
|
|
283
|
+
provider.enqueueReplies(dialect.generatedReply({ ...completion, text: matching }));
|
|
284
|
+
const structured = await invoke(structuredRequest(schema));
|
|
285
|
+
assert.equal(structured.kind, "branch");
|
|
286
|
+
if (structured.kind === "branch") {
|
|
287
|
+
assert.equal(structured.branch, "generated", structured.failure?.message);
|
|
288
|
+
assert.equal(structured.value.text, matching);
|
|
289
|
+
}
|
|
290
|
+
provider.enqueueReplies(dialect.generatedReply({ ...completion, text: `\`\`\`json
|
|
291
|
+
${matching}
|
|
292
|
+
\`\`\`` }));
|
|
293
|
+
const fenced = await invoke(structuredRequest(schema));
|
|
294
|
+
assert.equal(fenced.kind, "branch");
|
|
295
|
+
if (fenced.kind === "branch") {
|
|
296
|
+
assert.equal(fenced.branch, "generated");
|
|
297
|
+
assert.equal(fenced.value.text, matching);
|
|
298
|
+
}
|
|
299
|
+
provider.enqueueReplies(dialect.generatedReply({ ...completion, text: '{"answer":"forty-two","score":42}' }));
|
|
300
|
+
const mismatched = await invoke(structuredRequest(schema));
|
|
301
|
+
assert.equal(mismatched.kind, "branch");
|
|
302
|
+
if (mismatched.kind === "branch") {
|
|
303
|
+
assert.equal(mismatched.branch, "invalidResponse");
|
|
304
|
+
assert.ok(mismatched.failure?.message.includes("/score"));
|
|
305
|
+
}
|
|
306
|
+
const invalidSchema = { ...schema, properties: {
|
|
307
|
+
...schema.properties,
|
|
308
|
+
answer: { anyOf: [{ type: "string" }, { type: "integer" }] }
|
|
309
|
+
} };
|
|
310
|
+
const beforeInvalidSchema = provider.requests().length;
|
|
311
|
+
const invalid = await invoke(structuredRequest(invalidSchema));
|
|
312
|
+
assert.equal(invalid.kind, "branch");
|
|
313
|
+
if (invalid.kind === "branch") assert.equal(invalid.branch, "defect");
|
|
314
|
+
assert.equal(provider.requests().length, beforeInvalidSchema);
|
|
315
|
+
}
|
|
316
|
+
const invalidModelRequest = { ...basic(), model: "invalid model id" };
|
|
317
|
+
const countBeforeInvalidModel = provider.requests().length;
|
|
318
|
+
const invalidModel = await invoke(invalidModelRequest);
|
|
319
|
+
assert.equal(invalidModel.kind, "branch");
|
|
320
|
+
if (invalidModel.kind === "branch") assert.equal(invalidModel.branch, "defect");
|
|
321
|
+
assert.equal(provider.requests().length, countBeforeInvalidModel);
|
|
322
|
+
if (dialect.reportedErrorReply) {
|
|
323
|
+
provider.enqueueReplies(dialect.reportedErrorReply("textgentest_unmatched_error", providerMessageCanary));
|
|
324
|
+
const reported = await invoke(basic());
|
|
325
|
+
assert.equal(reported.kind, "branch");
|
|
326
|
+
if (reported.kind === "branch") assert.equal(reported.branch, "invalidResponse");
|
|
327
|
+
assert.equal(reported.failure?.message.includes(providerMessageCanary), false);
|
|
328
|
+
}
|
|
329
|
+
if (dialect.interruptedStreamReply) {
|
|
330
|
+
provider.enqueueReplies(dialect.interruptedStreamReply(completion));
|
|
331
|
+
const interrupted = await invoke(basic());
|
|
332
|
+
assert.equal(interrupted.kind, "retry");
|
|
333
|
+
if (interrupted.kind === "retry") assert.equal(interrupted.failure.kind, "TRANSPORT");
|
|
334
|
+
}
|
|
335
|
+
if (dialect.unstreamedReply) {
|
|
336
|
+
provider.enqueueReplies(dialect.unstreamedReply(completion));
|
|
337
|
+
const unstreamed = await invoke(basic());
|
|
338
|
+
assert.equal(unstreamed.kind, "branch");
|
|
339
|
+
if (unstreamed.kind === "branch") assert.equal(unstreamed.branch, "generated");
|
|
340
|
+
}
|
|
341
|
+
for (const rejectedRequest of suite.locallyRejectedRequests ?? []) {
|
|
342
|
+
const before = provider.requests().length;
|
|
343
|
+
const local = await invoke(rejectedRequest);
|
|
344
|
+
assert.equal(local.kind, "branch");
|
|
345
|
+
if (local.kind === "branch") assert.equal(local.branch, "defect");
|
|
346
|
+
assert.equal(provider.requests().length, before);
|
|
347
|
+
}
|
|
348
|
+
} finally {
|
|
349
|
+
await provider.close();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function resolveJSONPointer(body, pointer) {
|
|
353
|
+
if (!pointer.startsWith("/")) throw new TypeError("optional request pointer must begin with /");
|
|
354
|
+
let value = JSON.parse(body.toString());
|
|
355
|
+
for (const encoded of pointer.slice(1).split("/")) {
|
|
356
|
+
const segment = encoded.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
357
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
358
|
+
value = value[segment];
|
|
359
|
+
}
|
|
360
|
+
return value;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/textgen/textgentest/dex_scenarios.ts
|
|
364
|
+
import assert2 from "assert/strict";
|
|
365
|
+
import { randomUUID } from "crypto";
|
|
366
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
367
|
+
import { tmpdir } from "os";
|
|
368
|
+
import { join } from "path";
|
|
369
|
+
import {
|
|
370
|
+
Client,
|
|
371
|
+
Registry,
|
|
372
|
+
StepList,
|
|
373
|
+
Stream,
|
|
374
|
+
Worker,
|
|
375
|
+
forceComplete,
|
|
376
|
+
openBlobCache,
|
|
377
|
+
stringCodec
|
|
378
|
+
} from "@superdurable/dex";
|
|
379
|
+
var CompleteTextGeneration = class {
|
|
380
|
+
constructor(stepType) {
|
|
381
|
+
this.stepType = stepType;
|
|
382
|
+
}
|
|
383
|
+
stepType;
|
|
384
|
+
getStepType() {
|
|
385
|
+
return this.stepType;
|
|
386
|
+
}
|
|
387
|
+
execute(_context, result) {
|
|
388
|
+
return forceComplete(result);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
async function runTextGenerationDexScenarios(suite) {
|
|
392
|
+
if (!suite?.dialect || !suite.newQuery) throw new TypeError("text generation Dex scenario suite is incomplete");
|
|
393
|
+
const credential = "sk-textgentest-dex-canary";
|
|
394
|
+
const provider = await FakeProvider.start(suite.dialect.credentialHeader, credential);
|
|
395
|
+
const cacheDirectory = await mkdtemp(join(tmpdir(), "textgentest-dex-"));
|
|
396
|
+
const cache = openBlobCache({ directory: cacheDirectory, maxBytes: 64 << 20 });
|
|
397
|
+
let client;
|
|
398
|
+
let worker;
|
|
399
|
+
try {
|
|
400
|
+
const query = await suite.newQuery({
|
|
401
|
+
baseURL: provider.baseURL(),
|
|
402
|
+
model: suite.dialect.connectionModel,
|
|
403
|
+
apiKey: credential,
|
|
404
|
+
maxResponseBytes: 64 << 10,
|
|
405
|
+
requestTimeoutMs: 15e3
|
|
406
|
+
});
|
|
407
|
+
const unique = randomUUID().replaceAll("-", "");
|
|
408
|
+
const textStream = new Stream(`textgentest-text-${unique}`, stringCodec, 1 << 20);
|
|
409
|
+
const done = new CompleteTextGeneration(`TextgentestDone${unique}`);
|
|
410
|
+
const step = newQueryStep({
|
|
411
|
+
stepType: `TextgentestGenerate${unique}`,
|
|
412
|
+
annotations: { groupId: "textgentest", groupLabel: "Text Generation Test", explanation: "Generate a fixture answer." },
|
|
413
|
+
connection: { provider: "textgentest", name: "default" },
|
|
414
|
+
mapToOperationInput: (input2) => input2,
|
|
415
|
+
operation: query,
|
|
416
|
+
branches: [goToBranch("generated", done)],
|
|
417
|
+
textStream,
|
|
418
|
+
stepOptionsOverride: {
|
|
419
|
+
executeDurability: "sync",
|
|
420
|
+
executeMethodTimeoutMs: 2e4,
|
|
421
|
+
heartbeatTimeoutMs: 1e4,
|
|
422
|
+
executeRetry: {
|
|
423
|
+
initialIntervalMs: 1e3,
|
|
424
|
+
backoffCoefficient: 1,
|
|
425
|
+
maximumIntervalMs: 1e3,
|
|
426
|
+
maximumAttempts: 3,
|
|
427
|
+
totalDurationMs: 3e4
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
const flow = {
|
|
432
|
+
getFlowType: () => `TextgentestFlow${unique}`,
|
|
433
|
+
getSteps: () => StepList.startStep(step).otherSteps(done),
|
|
434
|
+
getPersistenceSchema: () => ({ streams: [textStream] })
|
|
435
|
+
};
|
|
436
|
+
const registry = new Registry([flow]);
|
|
437
|
+
const serverAddress = suite.serverAddress ?? process.env.DEX_SERVER_ADDRESS ?? "127.0.0.1:18891";
|
|
438
|
+
const workerOptions = { serverAddress, pull: { maxConcurrentTasks: 4, reservedRpcTasks: 0, stopDrainTimeoutMs: 100 } };
|
|
439
|
+
worker = new Worker(registry, cache, workerOptions);
|
|
440
|
+
client = new Client(registry, cache, { serverAddress });
|
|
441
|
+
await worker.start();
|
|
442
|
+
await worker.waitPullRegistered({ timeoutMs: 1e4 });
|
|
443
|
+
const completion = {
|
|
444
|
+
text: "The textgentest Dex answer.",
|
|
445
|
+
servedModel: "textgentest-served",
|
|
446
|
+
responseId: "textgentest-dex-1",
|
|
447
|
+
usage: { inputTokens: 11, cachedInputTokens: 3, outputTokens: 7, reasoningTokens: 2, totalTokens: 18 }
|
|
448
|
+
};
|
|
449
|
+
provider.enqueueReplies(suite.dialect.errorReply(429, "private retry message"), {
|
|
450
|
+
...suite.dialect.generatedReply(completion),
|
|
451
|
+
delayMs: 11e3
|
|
452
|
+
});
|
|
453
|
+
const firstFlowID = `textgentest-${randomUUID()}`;
|
|
454
|
+
const input = { model: suite.dialect.alternateModel, messages: [{ role: "user", text: "Say hello." }] };
|
|
455
|
+
await client.startFlow(flow, firstFlowID, input);
|
|
456
|
+
const generated = await client.waitForFlow(firstFlowID, 35e3);
|
|
457
|
+
assert2.equal(generated.status, "completed", generated.errorMessage ?? "text generation did not complete");
|
|
458
|
+
const generatedResult = generated.singleOutput().value;
|
|
459
|
+
assert2.equal(generatedResult.text, completion.text);
|
|
460
|
+
assert2.equal(generatedResult.requestedModel, suite.dialect.alternateModel);
|
|
461
|
+
assert2.equal(provider.requests().length, 2);
|
|
462
|
+
const firstRequest = provider.requests()[0];
|
|
463
|
+
const secondRequest = provider.requests()[1];
|
|
464
|
+
assert2.ok(firstRequest && secondRequest);
|
|
465
|
+
assert2.equal(suite.dialect.readRequestModel(firstRequest), suite.dialect.alternateModel);
|
|
466
|
+
assert2.equal(suite.dialect.readRequestModel(secondRequest), suite.dialect.alternateModel);
|
|
467
|
+
assert2.equal(firstRequest.hasCredentialInSlot && secondRequest.hasCredentialInSlot, true);
|
|
468
|
+
const textMessages = await waitForTextMessages(client, firstFlowID, textStream);
|
|
469
|
+
assert2.ok(textMessages.some((message) => message.includes(completion.text)));
|
|
470
|
+
provider.enqueueReplies(suite.dialect.truncatedReply({ ...completion, text: "partial" }));
|
|
471
|
+
const secondFlowID = `textgentest-${randomUUID()}`;
|
|
472
|
+
await client.startFlow(flow, secondFlowID, { messages: [{ role: "user", text: "Continue." }] });
|
|
473
|
+
const truncated = await client.waitForFlow(secondFlowID, 1e4);
|
|
474
|
+
assert2.equal(truncated.status, "failed");
|
|
475
|
+
assert2.equal(provider.requests().length, 3);
|
|
476
|
+
let expectedRequestsBeforeWorkerLoss = 3;
|
|
477
|
+
if (suite.dialect.interruptedStreamReply) {
|
|
478
|
+
const partial = "partial from interrupted attempt";
|
|
479
|
+
provider.enqueueReplies(
|
|
480
|
+
suite.dialect.interruptedStreamReply({ ...completion, text: partial }),
|
|
481
|
+
suite.dialect.generatedReply(completion)
|
|
482
|
+
);
|
|
483
|
+
const interruptedFlowID = `textgentest-${randomUUID()}`;
|
|
484
|
+
await client.startFlow(flow, interruptedFlowID, { messages: [{ role: "user", text: "Retry the stream." }] });
|
|
485
|
+
const completedAfterRetry = await client.waitForFlow(interruptedFlowID, 15e3);
|
|
486
|
+
assert2.equal(completedAfterRetry.status, "completed");
|
|
487
|
+
assert2.equal(completedAfterRetry.singleOutput().value.text, completion.text);
|
|
488
|
+
const text = await waitForTextMessages(client, interruptedFlowID, textStream, 2);
|
|
489
|
+
assert2.equal(text.reverse().join(""), partial + completion.text);
|
|
490
|
+
expectedRequestsBeforeWorkerLoss += 2;
|
|
491
|
+
assert2.equal(provider.requests().length, expectedRequestsBeforeWorkerLoss);
|
|
492
|
+
}
|
|
493
|
+
provider.enqueueReplies(
|
|
494
|
+
{ ...suite.dialect.generatedReply(completion), delayMs: 6e4 },
|
|
495
|
+
suite.dialect.generatedReply(completion)
|
|
496
|
+
);
|
|
497
|
+
const lostWorkerFlowID = `textgentest-${randomUUID()}`;
|
|
498
|
+
await client.startFlow(flow, lostWorkerFlowID, { messages: [{ role: "user", text: "Recover." }] });
|
|
499
|
+
await waitForRecordedRequests(provider, expectedRequestsBeforeWorkerLoss + 1);
|
|
500
|
+
await worker.close();
|
|
501
|
+
worker = new Worker(registry, cache, workerOptions);
|
|
502
|
+
await worker.start();
|
|
503
|
+
await worker.waitPullRegistered({ timeoutMs: 1e4 });
|
|
504
|
+
const recovered = await client.waitForFlow(lostWorkerFlowID, 35e3);
|
|
505
|
+
assert2.equal(recovered.status, "completed", recovered.errorMessage ?? "lost Worker request did not recover");
|
|
506
|
+
assert2.equal(recovered.singleOutput().value.text, completion.text);
|
|
507
|
+
assert2.equal(provider.requests().length, expectedRequestsBeforeWorkerLoss + 2);
|
|
508
|
+
} finally {
|
|
509
|
+
if (client) await client.close();
|
|
510
|
+
if (worker) await worker.close();
|
|
511
|
+
cache.close();
|
|
512
|
+
await rm(cacheDirectory, { recursive: true, force: true });
|
|
513
|
+
await provider.close();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async function waitForRecordedRequests(provider, count) {
|
|
517
|
+
const deadline = Date.now() + 1e4;
|
|
518
|
+
while (Date.now() < deadline) {
|
|
519
|
+
if (provider.requests().length >= count) return;
|
|
520
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
521
|
+
}
|
|
522
|
+
throw new Error("fake provider did not receive the expected request");
|
|
523
|
+
}
|
|
524
|
+
async function waitForTextMessages(client, flowID, stream, expectedCount = 1) {
|
|
525
|
+
const deadline = Date.now() + 5e3;
|
|
526
|
+
while (Date.now() < deadline) {
|
|
527
|
+
const page = await client.listStreamMessages(flowID, stream, 10);
|
|
528
|
+
if (page.messages.length >= expectedCount) return page.messages.map((message) => message.value);
|
|
529
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
530
|
+
}
|
|
531
|
+
throw new Error("text generation Stream did not converge");
|
|
532
|
+
}
|
|
533
|
+
export {
|
|
534
|
+
FakeProvider,
|
|
535
|
+
runTextGenerationDexScenarios,
|
|
536
|
+
runTextGenerationExchangeSuite
|
|
537
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { AsyncContext, Stream, BufferedTextStreamOptions } from '@superdurable/dex';
|
|
3
2
|
|
|
4
3
|
/** Provider or local fact independent of retry policy. */
|
|
5
4
|
declare const FailureKindSchema: z.ZodEnum<{
|
|
@@ -344,102 +343,4 @@ declare const ConnectorConfigurationRefSchema: z.ZodObject<{
|
|
|
344
343
|
/** Stable Flow and Step configuration owner. */
|
|
345
344
|
type ConnectorConfigurationRef = z.infer<typeof ConnectorConfigurationRefSchema>;
|
|
346
345
|
|
|
347
|
-
|
|
348
|
-
interface Progress {
|
|
349
|
-
/** Provider-specific stable phase. */ phase: string;
|
|
350
|
-
/** Optional safe progress text. */ message?: string;
|
|
351
|
-
/** Optional inclusive fraction from zero to one. */ percent?: number;
|
|
352
|
-
/** Optional provider-owned monotonic sequence. */ providerSequence?: number;
|
|
353
|
-
}
|
|
354
|
-
/** Structured best-effort Dex Stream message. */
|
|
355
|
-
interface ProgressUpdate extends Progress {
|
|
356
|
-
/** Stable connector call ID. */ callId: CallID;
|
|
357
|
-
/** One-based Dex Execute attempt. */ attempt: number;
|
|
358
|
-
/** Connector-assigned monotonic sequence within this attempt. */ sequence: number;
|
|
359
|
-
}
|
|
360
|
-
/** Optional application-owned Streams for one connector invocation. */
|
|
361
|
-
interface CallProgressOptions {
|
|
362
|
-
/** Registered structured progress Stream. */ progressStream?: Stream<ProgressUpdate>;
|
|
363
|
-
/** Registered string Stream for ordered buffered text. */ textStream?: Stream<string>;
|
|
364
|
-
/** Flush interval and soft UTF-8 batch limit for text. */ textOptions?: BufferedTextStreamOptions;
|
|
365
|
-
}
|
|
366
|
-
/** Provider-safe call metadata with its live, non-serializable Dex Step context. */
|
|
367
|
-
interface Call {
|
|
368
|
-
/** Active Dex Execute context; Step factories attach it as a non-enumerable field. */ context: AsyncContext;
|
|
369
|
-
/** Stable identity across retries. */ id: CallID;
|
|
370
|
-
/** Provider deduplication key, when available. */ idempotencyKey?: string;
|
|
371
|
-
/** Logical named connection. */ connection: ConnectionRef;
|
|
372
|
-
/** Manifest operation identity. */ operation: OperationRef;
|
|
373
|
-
/** Append structured progress when a Stream is configured. */ reportProgress(progress: Progress): void;
|
|
374
|
-
/** Append text in order when a buffered Stream is configured. */ writeText(value: string): void;
|
|
375
|
-
/** Whether structured progress output is configured. */ hasProgressStream(): boolean;
|
|
376
|
-
/** Whether buffered text output is configured. */ hasTextStream(): boolean;
|
|
377
|
-
}
|
|
378
|
-
/** Bind a live Dex Execute context to a stable, provider-safe call identity. */
|
|
379
|
-
declare function createCall(context: AsyncContext, connection: ConnectionRef, operation: OperationRef, options?: CallProgressOptions): Call;
|
|
380
|
-
/** Resolve a credential at call time outside durable Flow state. */
|
|
381
|
-
interface CredentialProvider<C> {
|
|
382
|
-
/** Return current credentials, honoring cancellation. */
|
|
383
|
-
resolve(call: Call, signal?: AbortSignal): Promise<C>;
|
|
384
|
-
}
|
|
385
|
-
/** Resolve credentials outside a Dex Step, such as for a Trigger source. */
|
|
386
|
-
interface ContextCredentialProvider<C> extends CredentialProvider<C> {
|
|
387
|
-
/** Return current credentials under an explicit cancellation signal. */
|
|
388
|
-
resolveContext(call: Call, signal: AbortSignal): Promise<C>;
|
|
389
|
-
}
|
|
390
|
-
/** Current private material and decision time for a refresh driver. */
|
|
391
|
-
interface CredentialRefreshState<C> {
|
|
392
|
-
/** Current connector-specific credential. */ credentials: C;
|
|
393
|
-
/** Known access credential expiry. */ expiresAt?: Date;
|
|
394
|
-
/** Captured decision time. */ now: Date;
|
|
395
|
-
}
|
|
396
|
-
/** Complete replacement credential and mandatory access expiry. */
|
|
397
|
-
interface CredentialRefreshResult<C> {
|
|
398
|
-
/** Complete replacement, retaining omitted renewal material. */ credentials: C;
|
|
399
|
-
/** Replacement access expiry. */ expiresAt: Date;
|
|
400
|
-
}
|
|
401
|
-
/** Connector-specific provider refresh contract; persistence belongs to the host. */
|
|
402
|
-
interface CredentialRefreshDriver<C> {
|
|
403
|
-
/** Decide deterministically whether a credential needs provider renewal. */
|
|
404
|
-
refreshRequired(state: CredentialRefreshState<C>): boolean;
|
|
405
|
-
/** Make one provider refresh request after the host admits it durably. */
|
|
406
|
-
refresh(state: CredentialRefreshState<C>, signal: AbortSignal): Promise<CredentialRefreshResult<C>>;
|
|
407
|
-
}
|
|
408
|
-
/** Host adapter that refreshes and persists a connection atomically. */
|
|
409
|
-
interface RefreshingCredentialProvider<C> extends CredentialProvider<C> {
|
|
410
|
-
/** Serialize refresh for one connection, reload state, and persist replacement. */
|
|
411
|
-
resolveWithRefresh(call: Call, driver: CredentialRefreshDriver<C>, signal?: AbortSignal): Promise<C>;
|
|
412
|
-
}
|
|
413
|
-
/** Host adapter for one explicit provider authentication rejection. */
|
|
414
|
-
interface RejectedCredentialRefreshingProvider<C> {
|
|
415
|
-
/** Force one coordinated refresh after the provider rejects an access token. */
|
|
416
|
-
resolveAfterRejection(call: Call, driver: CredentialRefreshDriver<C>, signal?: AbortSignal): Promise<C>;
|
|
417
|
-
}
|
|
418
|
-
/** Resolve through host refresh support, then context-aware or ordinary lookup. */
|
|
419
|
-
declare function resolveCredential<C>(provider: CredentialProvider<C>, call: Call, driver: CredentialRefreshDriver<C>, signal?: AbortSignal): Promise<C>;
|
|
420
|
-
/** Force one coordinated refresh after a provider authentication rejection. */
|
|
421
|
-
declare function resolveCredentialAfterRejection<C>(provider: CredentialProvider<C>, call: Call, driver: CredentialRefreshDriver<C>, signal?: AbortSignal): Promise<C>;
|
|
422
|
-
/** In-memory named credentials for examples and tests; never persisted by this class. */
|
|
423
|
-
declare class StaticCredentialProvider<C> implements RefreshingCredentialProvider<C> {
|
|
424
|
-
#private;
|
|
425
|
-
/** Capture all connection values during construction. */
|
|
426
|
-
constructor(entries: Iterable<readonly [ConnectionRef, C]>);
|
|
427
|
-
/** Return the configured credential or reject a missing connection. */
|
|
428
|
-
resolve(call: Call): Promise<C>;
|
|
429
|
-
/** Static credentials never expire and therefore do not call the driver. */
|
|
430
|
-
resolveWithRefresh(call: Call): Promise<C>;
|
|
431
|
-
}
|
|
432
|
-
/** Wrap a secret that cannot be serialized or printed. */
|
|
433
|
-
declare class SecretString {
|
|
434
|
-
#private;
|
|
435
|
-
/** Hold one private value outside default stringification and inspection. */
|
|
436
|
-
constructor(value: string);
|
|
437
|
-
/** Reveal only at the provider request boundary. */
|
|
438
|
-
reveal(): string;
|
|
439
|
-
/** Return a fixed redaction for accidental string formatting. */
|
|
440
|
-
toString(): string;
|
|
441
|
-
/** Reject accidental JSON persistence. */
|
|
442
|
-
toJSON(): never;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
export { StaticCredentialProvider as $, type MutationDefinitionWire as A, type BranchID as B, type Call as C, DefectBranchID as D, MutationDefinitionWireSchema as E, type Failure as F, MutationResultSchema as G, OperationRefSchema as H, type IdempotencyKey as I, type Progress as J, type QueryDefinitionWire as K, QueryDefinitionWireSchema as L, type MutationAttempt as M, QueryResultSchema as N, type OperationRef as O, type ProgressUpdate as P, type QueryAttempt as Q, ReauthorizationRequiredError as R, type Receipt as S, type ReceiptDraft as T, UncertainBranchID as U, ReceiptSchema as V, type RefreshingCredentialProvider as W, type RejectedCredentialRefreshingProvider as X, RetryError as Y, type RetryPolicy as Z, SecretString as _, type CallID as a, type StepDefaultsWire as a0, StepDefaultsWireSchema as a1, createCall as a2, defaultConnectorStepExecuteRetryPolicy as a3, defaultIdempotencyKey as a4, deriveCallID as a5, newMutationBranch as a6, newMutationRetry as a7, newMutationUncertain as a8, newQueryBranch as a9, newQueryRetry as aa, resolveCredential as ab, resolveCredentialAfterRejection as ac, retryPolicyToGoWire as ad, zeroReceipt as ae, type MutationResult as b, type QueryResult as c, type ConnectorConfigurationUI as d, type ConnectionRef as e, type BranchDefinition as f, BranchDefinitionSchema as g, CallIDNamespace as h, CallIDSchema as i, type CallProgressOptions as j, ConnectionRefSchema as k, type ConnectorConfigurationRef as l, ConnectorConfigurationRefSchema as m, ConnectorConfigurationUISchema as n, type ConnectorUIBinding as o, ConnectorUIBindingSchema as p, type ConnectorUIUnit as q, ConnectorUIUnitSchema as r, type ContextCredentialProvider as s, type CredentialProvider as t, type CredentialRefreshDriver as u, type CredentialRefreshResult as v, type CredentialRefreshState as w, type FailureKind as x, FailureKindSchema as y, FailureSchema as z };
|
|
346
|
+
export { type Receipt as A, type BranchID as B, type CallID as C, DefectBranchID as D, type ReceiptDraft as E, type Failure as F, ReceiptSchema as G, RetryError as H, type IdempotencyKey as I, type RetryPolicy as J, StepDefaultsWireSchema as K, defaultConnectorStepExecuteRetryPolicy as L, type MutationAttempt as M, defaultIdempotencyKey as N, type OperationRef as O, deriveCallID as P, type QueryAttempt as Q, ReauthorizationRequiredError as R, type StepDefaultsWire as S, newMutationBranch as T, UncertainBranchID as U, newMutationRetry as V, newMutationUncertain as W, newQueryBranch as X, newQueryRetry as Y, retryPolicyToGoWire as Z, zeroReceipt as _, type MutationResult as a, type QueryResult as b, type ConnectorConfigurationUI as c, type ConnectionRef as d, type BranchDefinition as e, BranchDefinitionSchema as f, CallIDNamespace as g, CallIDSchema as h, ConnectionRefSchema as i, type ConnectorConfigurationRef as j, ConnectorConfigurationRefSchema as k, ConnectorConfigurationUISchema as l, type ConnectorUIBinding as m, ConnectorUIBindingSchema as n, type ConnectorUIUnit as o, ConnectorUIUnitSchema as p, type FailureKind as q, FailureKindSchema as r, FailureSchema as s, type MutationDefinitionWire as t, MutationDefinitionWireSchema as u, MutationResultSchema as v, OperationRefSchema as w, type QueryDefinitionWire as x, QueryDefinitionWireSchema as y, QueryResultSchema as z };
|