@langchain/langgraph-sdk 1.9.20 → 1.9.22
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/client/base.cjs +4 -1
- package/dist/client/base.cjs.map +1 -1
- package/dist/client/base.d.cts +2 -0
- package/dist/client/base.d.cts.map +1 -1
- package/dist/client/base.d.ts +2 -0
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +5 -2
- package/dist/client/base.js.map +1 -1
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/runs/index.cjs +2 -0
- package/dist/client/runs/index.cjs.map +1 -1
- package/dist/client/runs/index.d.cts +15 -0
- package/dist/client/runs/index.d.cts.map +1 -1
- package/dist/client/runs/index.d.ts +15 -0
- package/dist/client/runs/index.d.ts.map +1 -1
- package/dist/client/runs/index.js +2 -0
- package/dist/client/runs/index.js.map +1 -1
- package/dist/client/stream/error.cjs +21 -0
- package/dist/client/stream/error.cjs.map +1 -1
- package/dist/client/stream/error.js +21 -1
- package/dist/client/stream/error.js.map +1 -1
- package/dist/client/stream/index.cjs +24 -1
- package/dist/client/stream/index.cjs.map +1 -1
- package/dist/client/stream/index.d.cts.map +1 -1
- package/dist/client/stream/index.d.ts.map +1 -1
- package/dist/client/stream/index.js +24 -1
- package/dist/client/stream/index.js.map +1 -1
- package/dist/client/stream/transport/agent-server.cjs +8 -2
- package/dist/client/stream/transport/agent-server.cjs.map +1 -1
- package/dist/client/stream/transport/agent-server.d.cts +17 -2
- package/dist/client/stream/transport/agent-server.d.cts.map +1 -1
- package/dist/client/stream/transport/agent-server.d.ts +17 -2
- package/dist/client/stream/transport/agent-server.d.ts.map +1 -1
- package/dist/client/stream/transport/agent-server.js +8 -2
- package/dist/client/stream/transport/agent-server.js.map +1 -1
- package/dist/client/stream/transport/http.cjs +81 -21
- package/dist/client/stream/transport/http.cjs.map +1 -1
- package/dist/client/stream/transport/http.d.cts +22 -7
- package/dist/client/stream/transport/http.d.cts.map +1 -1
- package/dist/client/stream/transport/http.d.ts +22 -7
- package/dist/client/stream/transport/http.d.ts.map +1 -1
- package/dist/client/stream/transport/http.js +83 -23
- package/dist/client/stream/transport/http.js.map +1 -1
- package/dist/client/stream/transport/index.cjs +2 -1
- package/dist/client/stream/transport/index.js +2 -1
- package/dist/client/stream/transport/types.d.cts +85 -5
- package/dist/client/stream/transport/types.d.cts.map +1 -1
- package/dist/client/stream/transport/types.d.ts +85 -5
- package/dist/client/stream/transport/types.d.ts.map +1 -1
- package/dist/client/stream/transport/utils.cjs +19 -0
- package/dist/client/stream/transport/utils.cjs.map +1 -1
- package/dist/client/stream/transport/utils.js +19 -1
- package/dist/client/stream/transport/utils.js.map +1 -1
- package/dist/client/stream/transport/websocket.cjs +125 -21
- package/dist/client/stream/transport/websocket.cjs.map +1 -1
- package/dist/client/stream/transport/websocket.d.cts +32 -4
- package/dist/client/stream/transport/websocket.d.cts.map +1 -1
- package/dist/client/stream/transport/websocket.d.ts +32 -4
- package/dist/client/stream/transport/websocket.d.ts.map +1 -1
- package/dist/client/stream/transport/websocket.js +126 -23
- package/dist/client/stream/transport/websocket.js.map +1 -1
- package/dist/client/stream/transport.d.cts +20 -3
- package/dist/client/stream/transport.d.cts.map +1 -1
- package/dist/client/stream/transport.d.ts +20 -3
- package/dist/client/stream/transport.d.ts.map +1 -1
- package/dist/client/stream/types.d.cts +31 -0
- package/dist/client/stream/types.d.cts.map +1 -1
- package/dist/client/stream/types.d.ts +31 -0
- package/dist/client/stream/types.d.ts.map +1 -1
- package/dist/client/threads/index.cjs +36 -17
- package/dist/client/threads/index.cjs.map +1 -1
- package/dist/client/threads/index.js +35 -16
- package/dist/client/threads/index.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/react-ui/server/server.cjs +2 -2
- package/dist/react-ui/server/server.cjs.map +1 -1
- package/dist/react-ui/server/server.js +1 -1
- package/dist/react-ui/server/server.js.map +1 -1
- package/dist/stream/controller.cjs +21 -3
- package/dist/stream/controller.cjs.map +1 -1
- package/dist/stream/controller.d.cts.map +1 -1
- package/dist/stream/controller.d.ts.map +1 -1
- package/dist/stream/controller.js +20 -2
- package/dist/stream/controller.js.map +1 -1
- package/dist/stream/index.cjs +2 -0
- package/dist/stream/index.d.cts +2 -1
- package/dist/stream/index.d.ts +2 -1
- package/dist/stream/index.js +2 -1
- package/dist/stream/projections/channel-effect.cjs +52 -0
- package/dist/stream/projections/channel-effect.cjs.map +1 -0
- package/dist/stream/projections/channel-effect.d.cts +35 -0
- package/dist/stream/projections/channel-effect.d.cts.map +1 -0
- package/dist/stream/projections/channel-effect.d.ts +35 -0
- package/dist/stream/projections/channel-effect.d.ts.map +1 -0
- package/dist/stream/projections/channel-effect.js +52 -0
- package/dist/stream/projections/channel-effect.js.map +1 -0
- package/dist/stream/projections/index.cjs +1 -0
- package/dist/stream/projections/index.d.ts +1 -0
- package/dist/stream/projections/index.js +1 -0
- package/dist/stream/root-message-projection.cjs +55 -0
- package/dist/stream/root-message-projection.cjs.map +1 -1
- package/dist/stream/root-message-projection.js +55 -0
- package/dist/stream/root-message-projection.js.map +1 -1
- package/dist/stream/submit-coordinator.cjs +3 -3
- package/dist/stream/submit-coordinator.cjs.map +1 -1
- package/dist/stream/submit-coordinator.js +1 -1
- package/dist/stream/submit-coordinator.js.map +1 -1
- package/dist/types.d.cts +20 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/branching.d.cts +1 -1
- package/dist/ui/branching.d.ts +1 -1
- package/dist/ui/orchestrator.d.cts +1 -1
- package/dist/ui/orchestrator.d.cts.map +1 -1
- package/dist/ui/orchestrator.d.ts +1 -1
- package/dist/ui/orchestrator.d.ts.map +1 -1
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/stream.cjs +94 -0
- package/dist/utils/stream.cjs.map +1 -1
- package/dist/utils/stream.d.cts +16 -2
- package/dist/utils/stream.d.cts.map +1 -1
- package/dist/utils/stream.d.ts +16 -2
- package/dist/utils/stream.d.ts.map +1 -1
- package/dist/utils/stream.js +94 -1
- package/dist/utils/stream.js.map +1 -1
- package/package.json +6 -5
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { BytesLineDecoder, SSEDecoder } from "../../../utils/sse.js";
|
|
2
|
-
import { IterableReadableStream } from "../../../utils/stream.js";
|
|
2
|
+
import { IterableReadableStream, idleReconnectStream } from "../../../utils/stream.js";
|
|
3
3
|
import { AsyncQueue } from "./queue.js";
|
|
4
|
-
import { isProtocolResponse, isRecord, mergeHeaders, toAbsoluteUrl, toError } from "./utils.js";
|
|
4
|
+
import { isProtocolResponse, isRecord, mergeHeaders, resolveProtocolPath, toAbsoluteUrl, toError } from "./utils.js";
|
|
5
|
+
import { webSocketReconnectDelayMs } from "./websocket.js";
|
|
5
6
|
//#region src/client/stream/transport/http.ts
|
|
6
7
|
/**
|
|
7
8
|
* Transport adapter that speaks the thread-centric protocol over HTTP
|
|
8
|
-
* commands plus SSE event streams. Bound to a
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* commands plus SSE event streams. Bound to a `threadId` at construction
|
|
10
|
+
* or later via {@link setThreadId}; request URLs derive from the
|
|
11
|
+
* currently-bound thread. Each {@link openEventStream} call opens an
|
|
12
|
+
* independent filtered SSE connection via
|
|
13
|
+
* `POST /threads/:thread_id/stream/events`.
|
|
11
14
|
*/
|
|
12
15
|
var ProtocolSseTransportAdapter = class {
|
|
13
16
|
threadId;
|
|
@@ -17,9 +20,12 @@ var ProtocolSseTransportAdapter = class {
|
|
|
17
20
|
defaultHeaders;
|
|
18
21
|
onRequest;
|
|
19
22
|
fetchFactory;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
asyncCaller;
|
|
24
|
+
maxReconnectAttempts;
|
|
25
|
+
idleReconnect;
|
|
26
|
+
onReconnect;
|
|
27
|
+
reconnectDelayMs;
|
|
28
|
+
paths;
|
|
23
29
|
sessionAbortController = new AbortController();
|
|
24
30
|
eventStreams = /* @__PURE__ */ new Set();
|
|
25
31
|
closed = false;
|
|
@@ -29,10 +35,31 @@ var ProtocolSseTransportAdapter = class {
|
|
|
29
35
|
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
30
36
|
this.onRequest = options.onRequest;
|
|
31
37
|
this.fetchFactory = options.fetchFactory;
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
38
|
+
this.asyncCaller = options.asyncCaller;
|
|
39
|
+
this.maxReconnectAttempts = options.fetch != null ? 0 : options.maxReconnectAttempts ?? 5;
|
|
40
|
+
this.idleReconnect = options.fetch != null ? null : options.idleReconnect ?? "auto";
|
|
41
|
+
this.onReconnect = options.onReconnect;
|
|
42
|
+
this.reconnectDelayMs = options.reconnectDelayMs ?? webSocketReconnectDelayMs;
|
|
43
|
+
this.threadId = options.threadId ?? "";
|
|
44
|
+
this.paths = options.paths;
|
|
45
|
+
}
|
|
46
|
+
/** {@inheritDoc TransportAdapter.setThreadId} */
|
|
47
|
+
setThreadId(threadId) {
|
|
48
|
+
this.threadId = threadId;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Command/stream/state URLs derive from the currently-bound thread so a
|
|
52
|
+
* single adapter can follow {@link setThreadId} re-binds. A fixed
|
|
53
|
+
* `paths.*` string overrides the default and is used as-is.
|
|
54
|
+
*/
|
|
55
|
+
get commandsUrl() {
|
|
56
|
+
return resolveProtocolPath(this.paths?.commands, this.threadId, (id) => `/threads/${id}/commands`);
|
|
57
|
+
}
|
|
58
|
+
get streamUrl() {
|
|
59
|
+
return resolveProtocolPath(this.paths?.stream, this.threadId, (id) => `/threads/${id}/stream/events`);
|
|
60
|
+
}
|
|
61
|
+
get stateUrl() {
|
|
62
|
+
return resolveProtocolPath(this.paths?.state, this.threadId, (id) => `/threads/${id}/state`);
|
|
36
63
|
}
|
|
37
64
|
/**
|
|
38
65
|
* Fetch checkpointed thread state for hydration.
|
|
@@ -108,9 +135,11 @@ var ProtocolSseTransportAdapter = class {
|
|
|
108
135
|
resolveReady = resolve;
|
|
109
136
|
rejectReady = reject;
|
|
110
137
|
});
|
|
111
|
-
|
|
138
|
+
let resumeAfterSeq = typeof params.since === "number" ? params.since : void 0;
|
|
139
|
+
let readySettled = false;
|
|
112
140
|
const startStream = async () => {
|
|
113
|
-
|
|
141
|
+
let attempt = 0;
|
|
142
|
+
while (!ac.signal.aborted && !this.closed) try {
|
|
114
143
|
const response = await this.request(streamUrl, {
|
|
115
144
|
method: "POST",
|
|
116
145
|
headers: {
|
|
@@ -121,30 +150,56 @@ var ProtocolSseTransportAdapter = class {
|
|
|
121
150
|
channels: params.channels,
|
|
122
151
|
...params.namespaces ? { namespaces: params.namespaces } : {},
|
|
123
152
|
...params.depth != null ? { depth: params.depth } : {},
|
|
124
|
-
...
|
|
153
|
+
...resumeAfterSeq != null ? { since: resumeAfterSeq } : {}
|
|
125
154
|
}),
|
|
126
155
|
signal: ac.signal
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
}, { stream: true });
|
|
157
|
+
if (!readySettled) {
|
|
158
|
+
readySettled = true;
|
|
159
|
+
resolveReady();
|
|
160
|
+
}
|
|
161
|
+
const readable = response.body ?? new ReadableStream({ start(controller) {
|
|
130
162
|
controller.close();
|
|
131
|
-
} })
|
|
163
|
+
} });
|
|
164
|
+
const enableIdle = this.idleReconnect === "auto" || typeof this.idleReconnect === "number" && this.idleReconnect > 0;
|
|
165
|
+
const lines = readable.pipeThrough(BytesLineDecoder());
|
|
166
|
+
const stream = (enableIdle ? lines.pipeThrough(idleReconnectStream({ mode: this.idleReconnect })) : lines).pipeThrough(SSEDecoder());
|
|
132
167
|
const iterable = IterableReadableStream.fromReadableStream(stream);
|
|
133
168
|
for await (const event of iterable) {
|
|
134
169
|
if (ac.signal.aborted || this.closed) break;
|
|
135
170
|
if (isRecord(event.data)) {
|
|
136
171
|
const msg = event.data;
|
|
172
|
+
if (typeof msg.seq === "number") resumeAfterSeq = msg.seq;
|
|
137
173
|
streamQueue.push(msg);
|
|
138
174
|
}
|
|
139
175
|
}
|
|
140
176
|
streamQueue.close();
|
|
177
|
+
return;
|
|
141
178
|
} catch (error) {
|
|
142
|
-
rejectReady(error);
|
|
143
179
|
if (ac.signal.aborted || this.closed) {
|
|
180
|
+
if (!readySettled) rejectReady(error);
|
|
144
181
|
streamQueue.close();
|
|
145
182
|
return;
|
|
146
183
|
}
|
|
147
|
-
|
|
184
|
+
if (this.maxReconnectAttempts <= 0) {
|
|
185
|
+
if (!readySettled) rejectReady(error);
|
|
186
|
+
streamQueue.close(toError(error));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
attempt += 1;
|
|
190
|
+
if (attempt > this.maxReconnectAttempts) {
|
|
191
|
+
if (!readySettled) rejectReady(error);
|
|
192
|
+
streamQueue.close(toError(error));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.onReconnect?.({
|
|
196
|
+
attempt,
|
|
197
|
+
cause: error
|
|
198
|
+
});
|
|
199
|
+
const delay = this.reconnectDelayMs(attempt);
|
|
200
|
+
if (delay > 0) await new Promise((resolve) => {
|
|
201
|
+
setTimeout(resolve, delay);
|
|
202
|
+
});
|
|
148
203
|
}
|
|
149
204
|
};
|
|
150
205
|
startStream();
|
|
@@ -176,16 +231,18 @@ var ProtocolSseTransportAdapter = class {
|
|
|
176
231
|
this.eventStreams.clear();
|
|
177
232
|
this.queue.close();
|
|
178
233
|
}
|
|
179
|
-
async request(path, init) {
|
|
234
|
+
async request(path, init, options) {
|
|
180
235
|
const url = toAbsoluteUrl(this.apiUrl, path);
|
|
181
236
|
let requestInit = {
|
|
182
237
|
...init,
|
|
183
238
|
headers: mergeHeaders(this.defaultHeaders, init.headers)
|
|
184
239
|
};
|
|
185
240
|
if (this.onRequest) requestInit = await this.onRequest(url, requestInit);
|
|
186
|
-
|
|
241
|
+
const useAsyncCaller = this.asyncCaller != null && !options?.stream;
|
|
242
|
+
const execute = async () => {
|
|
187
243
|
const response = await (await this.resolveFetch())(url.toString(), requestInit);
|
|
188
244
|
if (!response.ok) {
|
|
245
|
+
if (useAsyncCaller) throw response;
|
|
189
246
|
let detail = "";
|
|
190
247
|
try {
|
|
191
248
|
const body = await response.text();
|
|
@@ -197,6 +254,9 @@ var ProtocolSseTransportAdapter = class {
|
|
|
197
254
|
throw new Error(message);
|
|
198
255
|
}
|
|
199
256
|
return response;
|
|
257
|
+
};
|
|
258
|
+
try {
|
|
259
|
+
return useAsyncCaller ? await this.asyncCaller.call(execute) : await execute();
|
|
200
260
|
} catch (error) {
|
|
201
261
|
throw toError(error);
|
|
202
262
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n SubscribeParams,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n ProtocolSseTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter, EventStreamHandle } from \"../transport.js\";\nimport {\n toAbsoluteUrl,\n isRecord,\n mergeHeaders,\n toError,\n isProtocolResponse,\n} from \"./utils.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../../../utils/sse.js\";\nimport { IterableReadableStream } from \"../../../utils/stream.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over HTTP\n * commands plus SSE event streams. Bound to a specific `threadId`\n * at construction. Each {@link openEventStream} call opens an independent\n * filtered SSE connection via `POST /threads/:thread_id/stream/events`.\n */\nexport class ProtocolSseTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n readonly apiUrl: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly fetchImpl: typeof fetch;\n\n private readonly defaultHeaders: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly fetchFactory?: () => typeof fetch | Promise<typeof fetch>;\n\n private readonly commandsUrl: string;\n\n private readonly streamUrl: string;\n\n private readonly stateUrl: string;\n\n private readonly sessionAbortController = new AbortController();\n\n private readonly eventStreams = new Set<AbortController>();\n\n private closed = false;\n\n constructor(options: ProtocolSseTransportOptions) {\n this.fetchImpl = options.fetch ?? fetch;\n this.apiUrl = options.apiUrl;\n this.defaultHeaders = options.defaultHeaders ?? {};\n this.onRequest = options.onRequest;\n this.fetchFactory = options.fetchFactory;\n this.threadId = options.threadId;\n this.commandsUrl =\n options.paths?.commands ?? `/threads/${this.threadId}/commands`;\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n this.stateUrl = options.paths?.state ?? `/threads/${this.threadId}/state`;\n }\n\n /**\n * Fetch checkpointed thread state for hydration.\n *\n * Uses `GET`, matching `client.threads.getState()` and both LangGraph\n * Platform and Agent Protocol custom backends (`POST` is reserved for\n * `updateState`).\n */\n async getState<StateType = unknown>(): Promise<{\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n } | null> {\n const url = toAbsoluteUrl(this.apiUrl, this.stateUrl);\n let requestInit: RequestInit = {\n method: \"GET\",\n headers: mergeHeaders(this.defaultHeaders, {}),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (response.status === 404) return null;\n if (!response.ok) {\n const error = toError(\n new Error(\n `Thread state request failed: ${response.status} ${response.statusText}`\n )\n ) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n return (await response.json()) as {\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n };\n }\n\n private async resolveFetch(): Promise<typeof fetch> {\n if (this.fetchFactory) {\n return await this.fetchFactory();\n }\n return this.fetchImpl;\n }\n\n /**\n * HTTP/SSE transports have no handshake — connections are made\n * per-command and per-subscription.\n */\n async open(): Promise<void> {\n // no-op\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n const response = await this.request(this.commandsUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(command),\n signal: this.sessionAbortController.signal,\n });\n\n if (response.status === 202 || response.status === 204) {\n return undefined;\n }\n\n const payload = (await response.json()) as unknown;\n if (!isProtocolResponse(payload)) {\n throw new Error(\"Protocol command did not return a valid response.\");\n }\n return payload;\n }\n\n /**\n * WebSocket-style single event stream.\n * For the SSE transport this returns a dummy iterable; real event\n * delivery happens via {@link openEventStream}.\n */\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n openEventStream(params: SubscribeParams): EventStreamHandle {\n if (this.closed) {\n throw new Error(\"Protocol transport is closed.\");\n }\n\n const ac = new AbortController();\n this.eventStreams.add(ac);\n const streamQueue = new AsyncQueue<Message>();\n const streamUrl = this.streamUrl;\n\n let resolveReady!: () => void;\n let rejectReady!: (err: unknown) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n\n const since = (params as SubscribeParams & { since?: unknown }).since;\n\n const startStream = async () => {\n try {\n const response = await this.request(streamUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n channels: params.channels,\n ...(params.namespaces ? { namespaces: params.namespaces } : {}),\n ...(params.depth != null ? { depth: params.depth } : {}),\n ...(typeof since === \"number\" ? { since } : {}),\n }),\n signal: ac.signal,\n });\n\n resolveReady();\n\n const readable =\n response.body ??\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n },\n });\n\n const stream = readable\n .pipeThrough(BytesLineDecoder())\n .pipeThrough(SSEDecoder());\n const iterable = IterableReadableStream.fromReadableStream(stream);\n\n for await (const event of iterable) {\n if (ac.signal.aborted || this.closed) {\n break;\n }\n if (isRecord(event.data)) {\n const msg = event.data as Message & {\n seq?: number;\n method?: string;\n };\n streamQueue.push(msg);\n }\n }\n streamQueue.close();\n } catch (error) {\n rejectReady(error);\n if (ac.signal.aborted || this.closed) {\n streamQueue.close();\n return;\n }\n streamQueue.close(error);\n }\n };\n\n void startStream();\n\n const cleanup = () => {\n this.eventStreams.delete(ac);\n ac.abort();\n streamQueue.close();\n };\n\n return {\n events: {\n [Symbol.asyncIterator]: () => ({\n next: async () => await streamQueue.shift(),\n return: async () => {\n cleanup();\n return { done: true, value: undefined };\n },\n }),\n },\n ready,\n close: cleanup,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.sessionAbortController.abort();\n for (const ac of this.eventStreams) ac.abort();\n this.eventStreams.clear();\n this.queue.close();\n }\n\n private async request(path: string, init: RequestInit): Promise<Response> {\n const url = toAbsoluteUrl(this.apiUrl, path);\n let requestInit: RequestInit = {\n ...init,\n headers: mergeHeaders(this.defaultHeaders, init.headers),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n try {\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (!response.ok) {\n let detail = \"\";\n try {\n const body = await response.text();\n const parsed = JSON.parse(body);\n if (typeof parsed === \"object\" && parsed != null) {\n detail =\n ((parsed as Record<string, unknown>).message as string) ??\n ((parsed as Record<string, unknown>).error as string) ??\n \"\";\n }\n if (!detail) detail = body;\n } catch {\n // body unreadable or not JSON — fall through\n }\n const message = detail\n ? `Protocol request failed: ${response.status} ${response.statusText} — ${detail}`\n : `Protocol request failed: ${response.status} ${response.statusText}`;\n throw new Error(message);\n }\n return response;\n } catch (error) {\n throw toError(error);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http.js","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n SubscribeParams,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport type { AsyncCaller } from \"../../../utils/async_caller.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n ProtocolSseTransportOptions,\n ProtocolTransportPaths,\n} from \"./types.js\";\nimport type { TransportAdapter, EventStreamHandle } from \"../transport.js\";\nimport {\n toAbsoluteUrl,\n isRecord,\n mergeHeaders,\n toError,\n isProtocolResponse,\n resolveProtocolPath,\n} from \"./utils.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../../../utils/sse.js\";\nimport {\n IterableReadableStream,\n idleReconnectStream,\n type IdleReconnectMode,\n} from \"../../../utils/stream.js\";\nimport { webSocketReconnectDelayMs } from \"./websocket.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over HTTP\n * commands plus SSE event streams. Bound to a `threadId` at construction\n * or later via {@link setThreadId}; request URLs derive from the\n * currently-bound thread. Each {@link openEventStream} call opens an\n * independent filtered SSE connection via\n * `POST /threads/:thread_id/stream/events`.\n */\nexport class ProtocolSseTransportAdapter implements TransportAdapter {\n threadId: string;\n\n readonly apiUrl: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly fetchImpl: typeof fetch;\n\n private readonly defaultHeaders: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly fetchFactory?: () => typeof fetch | Promise<typeof fetch>;\n\n private readonly asyncCaller?: AsyncCaller;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly idleReconnect: IdleReconnectMode | null;\n\n private readonly onReconnect?: ProtocolSseTransportOptions[\"onReconnect\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private readonly paths?: ProtocolTransportPaths;\n\n private readonly sessionAbortController = new AbortController();\n\n private readonly eventStreams = new Set<AbortController>();\n\n private closed = false;\n\n constructor(options: ProtocolSseTransportOptions) {\n this.fetchImpl = options.fetch ?? fetch;\n this.apiUrl = options.apiUrl;\n this.defaultHeaders = options.defaultHeaders ?? {};\n this.onRequest = options.onRequest;\n this.fetchFactory = options.fetchFactory;\n this.asyncCaller = options.asyncCaller;\n // Custom fetch (tests/mocks) must not auto-reconnect — same policy as skipping AsyncCaller.\n this.maxReconnectAttempts =\n options.fetch != null ? 0 : (options.maxReconnectAttempts ?? 5);\n // Custom fetch (tests/mocks) also disables the idle watchdog, matching the\n // no-auto-reconnect policy above — a tripped watchdog would have nothing to\n // reconnect to and would surface spurious errors in those harnesses.\n // Otherwise default to heartbeat-adaptive `\"auto\"`, which stays dormant\n // unless the server actually emits keep-alive heartbeats.\n this.idleReconnect =\n options.fetch != null ? null : (options.idleReconnect ?? \"auto\");\n this.onReconnect = options.onReconnect;\n this.reconnectDelayMs =\n options.reconnectDelayMs ?? webSocketReconnectDelayMs;\n this.threadId = options.threadId ?? \"\";\n this.paths = options.paths;\n }\n\n /** {@inheritDoc TransportAdapter.setThreadId} */\n setThreadId(threadId: string): void {\n this.threadId = threadId;\n }\n\n /**\n * Command/stream/state URLs derive from the currently-bound thread so a\n * single adapter can follow {@link setThreadId} re-binds. A fixed\n * `paths.*` string overrides the default and is used as-is.\n */\n private get commandsUrl(): string {\n return resolveProtocolPath(\n this.paths?.commands,\n this.threadId,\n (id) => `/threads/${id}/commands`\n );\n }\n\n private get streamUrl(): string {\n return resolveProtocolPath(\n this.paths?.stream,\n this.threadId,\n (id) => `/threads/${id}/stream/events`\n );\n }\n\n private get stateUrl(): string {\n return resolveProtocolPath(\n this.paths?.state,\n this.threadId,\n (id) => `/threads/${id}/state`\n );\n }\n\n /**\n * Fetch checkpointed thread state for hydration.\n *\n * Uses `GET`, matching `client.threads.getState()` and both LangGraph\n * Platform and Agent Protocol custom backends (`POST` is reserved for\n * `updateState`).\n */\n async getState<StateType = unknown>(): Promise<{\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n } | null> {\n const url = toAbsoluteUrl(this.apiUrl, this.stateUrl);\n let requestInit: RequestInit = {\n method: \"GET\",\n headers: mergeHeaders(this.defaultHeaders, {}),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (response.status === 404) return null;\n if (!response.ok) {\n const error = toError(\n new Error(\n `Thread state request failed: ${response.status} ${response.statusText}`\n )\n ) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n return (await response.json()) as {\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n };\n }\n\n private async resolveFetch(): Promise<typeof fetch> {\n if (this.fetchFactory) {\n return await this.fetchFactory();\n }\n return this.fetchImpl;\n }\n\n /**\n * HTTP/SSE transports have no handshake — connections are made\n * per-command and per-subscription.\n */\n async open(): Promise<void> {\n // no-op\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n const response = await this.request(this.commandsUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(command),\n signal: this.sessionAbortController.signal,\n });\n\n if (response.status === 202 || response.status === 204) {\n return undefined;\n }\n\n const payload = (await response.json()) as unknown;\n if (!isProtocolResponse(payload)) {\n throw new Error(\"Protocol command did not return a valid response.\");\n }\n return payload;\n }\n\n /**\n * WebSocket-style single event stream.\n * For the SSE transport this returns a dummy iterable; real event\n * delivery happens via {@link openEventStream}.\n */\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n openEventStream(params: SubscribeParams): EventStreamHandle {\n if (this.closed) {\n throw new Error(\"Protocol transport is closed.\");\n }\n\n const ac = new AbortController();\n this.eventStreams.add(ac);\n const streamQueue = new AsyncQueue<Message>();\n const streamUrl = this.streamUrl;\n\n let resolveReady!: () => void;\n let rejectReady!: (err: unknown) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n\n let resumeAfterSeq =\n typeof (params as SubscribeParams & { since?: unknown }).since ===\n \"number\"\n ? (params as SubscribeParams & { since: number }).since\n : undefined;\n\n let readySettled = false;\n\n const startStream = async () => {\n let attempt = 0;\n\n while (!ac.signal.aborted && !this.closed) {\n try {\n const response = await this.request(\n streamUrl,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n channels: params.channels,\n ...(params.namespaces ? { namespaces: params.namespaces } : {}),\n ...(params.depth != null ? { depth: params.depth } : {}),\n ...(resumeAfterSeq != null ? { since: resumeAfterSeq } : {}),\n }),\n signal: ac.signal,\n },\n { stream: true }\n );\n\n if (!readySettled) {\n readySettled = true;\n resolveReady();\n }\n\n const readable =\n response.body ??\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n },\n });\n\n // Idle watchdog on the line stream (between byte-line and SSE\n // decoding) so it can reset on any line and recognise `:` keep-alive\n // heartbeats to drive `\"auto\"` mode. On idle it errors the stream,\n // which the catch below treats like any other disconnect and\n // reconnects with `since` from the last seen sequence.\n const enableIdle =\n this.idleReconnect === \"auto\" ||\n (typeof this.idleReconnect === \"number\" && this.idleReconnect > 0);\n const lines = readable.pipeThrough(BytesLineDecoder());\n const watched = enableIdle\n ? lines.pipeThrough(\n idleReconnectStream({ mode: this.idleReconnect! })\n )\n : lines;\n const stream = watched.pipeThrough(SSEDecoder());\n const iterable = IterableReadableStream.fromReadableStream(stream);\n\n for await (const event of iterable) {\n if (ac.signal.aborted || this.closed) {\n break;\n }\n if (isRecord(event.data)) {\n const msg = event.data as Message & {\n seq?: number;\n method?: string;\n };\n if (typeof msg.seq === \"number\") {\n resumeAfterSeq = msg.seq;\n }\n streamQueue.push(msg);\n }\n }\n streamQueue.close();\n return;\n } catch (error) {\n if (ac.signal.aborted || this.closed) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close();\n return;\n }\n if (this.maxReconnectAttempts <= 0) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n attempt += 1;\n if (attempt > this.maxReconnectAttempts) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n this.onReconnect?.({ attempt, cause: error });\n const delay = this.reconnectDelayMs(attempt);\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n }\n };\n\n void startStream();\n\n const cleanup = () => {\n this.eventStreams.delete(ac);\n ac.abort();\n streamQueue.close();\n };\n\n return {\n events: {\n [Symbol.asyncIterator]: () => ({\n next: async () => await streamQueue.shift(),\n return: async () => {\n cleanup();\n return { done: true, value: undefined };\n },\n }),\n },\n ready,\n close: cleanup,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.sessionAbortController.abort();\n for (const ac of this.eventStreams) ac.abort();\n this.eventStreams.clear();\n this.queue.close();\n }\n\n private async request(\n path: string,\n init: RequestInit,\n options?: { stream?: boolean }\n ): Promise<Response> {\n const url = toAbsoluteUrl(this.apiUrl, path);\n let requestInit: RequestInit = {\n ...init,\n headers: mergeHeaders(this.defaultHeaders, init.headers),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n // Long-lived SSE event streams must not run through AsyncCaller: its\n // p-queue/p-retry semantics are designed for discrete request/response\n // calls, and wrapping a streaming response stalls the call (and can\n // leak retries). Stream resilience is handled separately by the\n // reconnect loop in `openEventStream`.\n const useAsyncCaller = this.asyncCaller != null && !options?.stream;\n\n const execute = async (): Promise<Response> => {\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (!response.ok) {\n // Reject with the Response so AsyncCaller maps it to HTTPError and\n // applies STATUS_NO_RETRY / retry policy consistently with REST.\n if (useAsyncCaller) {\n throw response;\n }\n let detail = \"\";\n try {\n const body = await response.text();\n const parsed = JSON.parse(body);\n if (typeof parsed === \"object\" && parsed != null) {\n detail =\n ((parsed as Record<string, unknown>).message as string) ??\n ((parsed as Record<string, unknown>).error as string) ??\n \"\";\n }\n if (!detail) detail = body;\n } catch {\n // body unreadable or not JSON — fall through\n }\n const message = detail\n ? `Protocol request failed: ${response.status} ${response.statusText} — ${detail}`\n : `Protocol request failed: ${response.status} ${response.statusText}`;\n throw new Error(message);\n }\n return response;\n };\n\n try {\n return useAsyncCaller\n ? await this.asyncCaller!.call(execute)\n : await execute();\n } catch (error) {\n throw toError(error);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,29 +1,109 @@
|
|
|
1
|
+
import { AsyncCaller } from "../../../utils/async_caller.cjs";
|
|
2
|
+
import { IdleReconnectMode } from "../../../utils/stream.cjs";
|
|
1
3
|
import { CommandResponse, ErrorResponse } from "@langchain/protocol";
|
|
2
4
|
|
|
3
5
|
//#region src/client/stream/transport/types.d.ts
|
|
4
6
|
type ProtocolRequestHook = (url: URL, init: RequestInit) => Promise<RequestInit> | RequestInit;
|
|
7
|
+
/**
|
|
8
|
+
* A protocol request path. Either a fixed string (bound to a specific
|
|
9
|
+
* thread) or a function of the active threadId. Use the function form
|
|
10
|
+
* when a single adapter instance must follow {@link TransportAdapter.setThreadId}
|
|
11
|
+
* re-binds — e.g. lazy thread creation, where the id is only known after
|
|
12
|
+
* the first `submit()`.
|
|
13
|
+
*/
|
|
14
|
+
type ProtocolPath = string | ((threadId: string) => string);
|
|
5
15
|
interface ProtocolTransportPaths {
|
|
6
|
-
commands?:
|
|
7
|
-
stream?:
|
|
16
|
+
commands?: ProtocolPath;
|
|
17
|
+
stream?: ProtocolPath;
|
|
8
18
|
/** `GET` path for thread-state hydration. Defaults to `/threads/:threadId/state`. */
|
|
9
|
-
state?:
|
|
19
|
+
state?: ProtocolPath;
|
|
10
20
|
}
|
|
11
21
|
interface ProtocolSseTransportOptions {
|
|
12
22
|
apiUrl: string;
|
|
13
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Thread this transport targets. Optional: omit to construct an
|
|
25
|
+
* unbound transport and bind later via {@link TransportAdapter.setThreadId}
|
|
26
|
+
* (the framework does this from `client.threads.stream`). Requests throw
|
|
27
|
+
* until a thread is bound.
|
|
28
|
+
*/
|
|
29
|
+
threadId?: string;
|
|
14
30
|
defaultHeaders?: Record<string, HeaderValue>;
|
|
15
31
|
onRequest?: ProtocolRequestHook;
|
|
16
32
|
fetch?: typeof fetch;
|
|
17
33
|
fetchFactory?: () => typeof fetch | Promise<typeof fetch>;
|
|
34
|
+
/**
|
|
35
|
+
* When set, command and SSE subscription HTTP requests are executed
|
|
36
|
+
* through {@link AsyncCaller} (retries, concurrency). Typically wired
|
|
37
|
+
* from {@link BaseClient} via `client.threads.stream()`.
|
|
38
|
+
*/
|
|
39
|
+
asyncCaller?: AsyncCaller;
|
|
18
40
|
paths?: ProtocolTransportPaths;
|
|
41
|
+
/**
|
|
42
|
+
* Maximum reconnect attempts after an unexpected SSE disconnect.
|
|
43
|
+
* Defaults to 5. Set to 0 to disable automatic reconnection.
|
|
44
|
+
*/
|
|
45
|
+
maxReconnectAttempts?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Idle-reconnect policy guarding against half-open sockets that hang
|
|
48
|
+
* indefinitely with no error or close (e.g. a platform revision rollover
|
|
49
|
+
* that hard-kills the serving pod). On idle the underlying read is aborted,
|
|
50
|
+
* which the reconnect loop treats like any other disconnect, re-subscribing
|
|
51
|
+
* with `since` from the last seen sequence.
|
|
52
|
+
*
|
|
53
|
+
* - `"auto"`: arm only once the server's SSE keep-alive heartbeats
|
|
54
|
+
* (LangGraph Platform: `: heartbeat` every ~5s) are observed, sizing the
|
|
55
|
+
* window from their cadence. Independent of agent activity; stays dormant
|
|
56
|
+
* on heartbeat-less servers.
|
|
57
|
+
* - a `number`: a fixed idle window in milliseconds.
|
|
58
|
+
* - `0`: disables it.
|
|
59
|
+
*
|
|
60
|
+
* @see {@link IdleReconnectMode}
|
|
61
|
+
*/
|
|
62
|
+
idleReconnect?: IdleReconnectMode;
|
|
63
|
+
/** Called before each SSE reconnect attempt (after backoff delay). */
|
|
64
|
+
onReconnect?: (options: {
|
|
65
|
+
attempt: number;
|
|
66
|
+
cause: unknown;
|
|
67
|
+
}) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Backoff before each SSE reconnect attempt. Defaults to
|
|
70
|
+
* {@link webSocketReconnectDelayMs} from `./websocket.js`.
|
|
71
|
+
*/
|
|
72
|
+
reconnectDelayMs?: (attempt: number) => number;
|
|
19
73
|
}
|
|
20
74
|
interface ProtocolWebSocketTransportOptions {
|
|
21
75
|
apiUrl: string;
|
|
22
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Thread this transport targets. Optional: omit to construct an
|
|
78
|
+
* unbound transport and bind later via {@link TransportAdapter.setThreadId}.
|
|
79
|
+
*/
|
|
80
|
+
threadId?: string;
|
|
23
81
|
defaultHeaders?: Record<string, HeaderValue>;
|
|
24
82
|
onRequest?: ProtocolRequestHook;
|
|
25
83
|
webSocketFactory?: (url: string) => WebSocket;
|
|
26
84
|
paths?: Pick<ProtocolTransportPaths, "stream">;
|
|
85
|
+
/**
|
|
86
|
+
* Maximum reconnect attempts after an unexpected socket close.
|
|
87
|
+
* Defaults to 5. Set to 0 to disable automatic reconnection.
|
|
88
|
+
*/
|
|
89
|
+
maxReconnectAttempts?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Called before each reconnect attempt (after backoff delay).
|
|
92
|
+
*/
|
|
93
|
+
onReconnect?: (options: {
|
|
94
|
+
attempt: number;
|
|
95
|
+
cause: unknown;
|
|
96
|
+
}) => void;
|
|
97
|
+
/**
|
|
98
|
+
* Invoked after the socket has been re-established. Use to restore
|
|
99
|
+
* server-side subscription state (see `ThreadStream`).
|
|
100
|
+
*/
|
|
101
|
+
onReconnected?: () => void | Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Backoff before each reconnect attempt. Defaults to
|
|
104
|
+
* {@link webSocketReconnectDelayMs}.
|
|
105
|
+
*/
|
|
106
|
+
reconnectDelayMs?: (attempt: number) => number;
|
|
27
107
|
}
|
|
28
108
|
type HeaderValue = string | undefined | null;
|
|
29
109
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.cts","names":[],"sources":["../../../../src/client/stream/transport/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.cts","names":[],"sources":["../../../../src/client/stream/transport/types.ts"],"mappings":";;;;;KAIY,mBAAA,IACV,GAAA,EAAK,GAAA,EACL,IAAA,EAAM,WAAA,KACH,OAAA,CAAQ,WAAA,IAAe,WAAA;;AAH5B;;;;;;KAYY,YAAA,cAA0B,QAAA;AAAA,UAErB,sBAAA;EACf,QAAA,GAAW,YAAA;EACX,MAAA,GAAS,YAAA;EAfJ;EAiBL,KAAA,GAAQ,YAAA;AAAA;AAAA,UAGO,2BAAA;EACf,MAAA;EAnBW;;;;AASb;;EAiBE,QAAA;EACA,cAAA,GAAiB,MAAA,SAAe,WAAA;EAChC,SAAA,GAAY,mBAAA;EACZ,KAAA,UAAe,KAAA;EACf,YAAA,gBAA4B,KAAA,GAAQ,OAAA,QAAe,KAAA;;;;;;EAMnD,WAAA,GAAc,WAAA;EACd,KAAA,GAAQ,sBAAA;EAzBG;;;;EA8BX,oBAAA;EA3BoB;;AAGtB;;;;;;;;;;;;;;EAyCE,aAAA,GAAgB,iBAAA;EAxChB;EA0CA,WAAA,IAAe,OAAA;IAAW,OAAA;IAAiB,KAAA;EAAA;EAjC3C;;;;EAsCA,gBAAA,IAAoB,OAAA;AAAA;AAAA,UAGL,iCAAA;EACf,MAAA;EAlCA;;;;EAuCA,QAAA;EACA,cAAA,GAAiB,MAAA,SAAe,WAAA;EAChC,SAAA,GAAY,mBAAA;EACZ,gBAAA,IAAoB,GAAA,aAAgB,SAAA;EACpC,KAAA,GAAQ,IAAA,CAAK,sBAAA;EAlB8B;;;;EAuB3C,oBAAA;EAlBmC;AAGrC;;EAmBE,WAAA,IAAe,OAAA;IAAW,OAAA;IAAiB,KAAA;EAAA;EAVP;;;;EAepC,aAAA,gBAA6B,OAAA;EAAO;;;;EAKpC,gBAAA,IAAoB,OAAA;AAAA;AAAA,KAGV,WAAA"}
|
|
@@ -1,29 +1,109 @@
|
|
|
1
|
+
import { AsyncCaller } from "../../../utils/async_caller.js";
|
|
2
|
+
import { IdleReconnectMode } from "../../../utils/stream.js";
|
|
1
3
|
import { CommandResponse, ErrorResponse } from "@langchain/protocol";
|
|
2
4
|
|
|
3
5
|
//#region src/client/stream/transport/types.d.ts
|
|
4
6
|
type ProtocolRequestHook = (url: URL, init: RequestInit) => Promise<RequestInit> | RequestInit;
|
|
7
|
+
/**
|
|
8
|
+
* A protocol request path. Either a fixed string (bound to a specific
|
|
9
|
+
* thread) or a function of the active threadId. Use the function form
|
|
10
|
+
* when a single adapter instance must follow {@link TransportAdapter.setThreadId}
|
|
11
|
+
* re-binds — e.g. lazy thread creation, where the id is only known after
|
|
12
|
+
* the first `submit()`.
|
|
13
|
+
*/
|
|
14
|
+
type ProtocolPath = string | ((threadId: string) => string);
|
|
5
15
|
interface ProtocolTransportPaths {
|
|
6
|
-
commands?:
|
|
7
|
-
stream?:
|
|
16
|
+
commands?: ProtocolPath;
|
|
17
|
+
stream?: ProtocolPath;
|
|
8
18
|
/** `GET` path for thread-state hydration. Defaults to `/threads/:threadId/state`. */
|
|
9
|
-
state?:
|
|
19
|
+
state?: ProtocolPath;
|
|
10
20
|
}
|
|
11
21
|
interface ProtocolSseTransportOptions {
|
|
12
22
|
apiUrl: string;
|
|
13
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Thread this transport targets. Optional: omit to construct an
|
|
25
|
+
* unbound transport and bind later via {@link TransportAdapter.setThreadId}
|
|
26
|
+
* (the framework does this from `client.threads.stream`). Requests throw
|
|
27
|
+
* until a thread is bound.
|
|
28
|
+
*/
|
|
29
|
+
threadId?: string;
|
|
14
30
|
defaultHeaders?: Record<string, HeaderValue>;
|
|
15
31
|
onRequest?: ProtocolRequestHook;
|
|
16
32
|
fetch?: typeof fetch;
|
|
17
33
|
fetchFactory?: () => typeof fetch | Promise<typeof fetch>;
|
|
34
|
+
/**
|
|
35
|
+
* When set, command and SSE subscription HTTP requests are executed
|
|
36
|
+
* through {@link AsyncCaller} (retries, concurrency). Typically wired
|
|
37
|
+
* from {@link BaseClient} via `client.threads.stream()`.
|
|
38
|
+
*/
|
|
39
|
+
asyncCaller?: AsyncCaller;
|
|
18
40
|
paths?: ProtocolTransportPaths;
|
|
41
|
+
/**
|
|
42
|
+
* Maximum reconnect attempts after an unexpected SSE disconnect.
|
|
43
|
+
* Defaults to 5. Set to 0 to disable automatic reconnection.
|
|
44
|
+
*/
|
|
45
|
+
maxReconnectAttempts?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Idle-reconnect policy guarding against half-open sockets that hang
|
|
48
|
+
* indefinitely with no error or close (e.g. a platform revision rollover
|
|
49
|
+
* that hard-kills the serving pod). On idle the underlying read is aborted,
|
|
50
|
+
* which the reconnect loop treats like any other disconnect, re-subscribing
|
|
51
|
+
* with `since` from the last seen sequence.
|
|
52
|
+
*
|
|
53
|
+
* - `"auto"`: arm only once the server's SSE keep-alive heartbeats
|
|
54
|
+
* (LangGraph Platform: `: heartbeat` every ~5s) are observed, sizing the
|
|
55
|
+
* window from their cadence. Independent of agent activity; stays dormant
|
|
56
|
+
* on heartbeat-less servers.
|
|
57
|
+
* - a `number`: a fixed idle window in milliseconds.
|
|
58
|
+
* - `0`: disables it.
|
|
59
|
+
*
|
|
60
|
+
* @see {@link IdleReconnectMode}
|
|
61
|
+
*/
|
|
62
|
+
idleReconnect?: IdleReconnectMode;
|
|
63
|
+
/** Called before each SSE reconnect attempt (after backoff delay). */
|
|
64
|
+
onReconnect?: (options: {
|
|
65
|
+
attempt: number;
|
|
66
|
+
cause: unknown;
|
|
67
|
+
}) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Backoff before each SSE reconnect attempt. Defaults to
|
|
70
|
+
* {@link webSocketReconnectDelayMs} from `./websocket.js`.
|
|
71
|
+
*/
|
|
72
|
+
reconnectDelayMs?: (attempt: number) => number;
|
|
19
73
|
}
|
|
20
74
|
interface ProtocolWebSocketTransportOptions {
|
|
21
75
|
apiUrl: string;
|
|
22
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Thread this transport targets. Optional: omit to construct an
|
|
78
|
+
* unbound transport and bind later via {@link TransportAdapter.setThreadId}.
|
|
79
|
+
*/
|
|
80
|
+
threadId?: string;
|
|
23
81
|
defaultHeaders?: Record<string, HeaderValue>;
|
|
24
82
|
onRequest?: ProtocolRequestHook;
|
|
25
83
|
webSocketFactory?: (url: string) => WebSocket;
|
|
26
84
|
paths?: Pick<ProtocolTransportPaths, "stream">;
|
|
85
|
+
/**
|
|
86
|
+
* Maximum reconnect attempts after an unexpected socket close.
|
|
87
|
+
* Defaults to 5. Set to 0 to disable automatic reconnection.
|
|
88
|
+
*/
|
|
89
|
+
maxReconnectAttempts?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Called before each reconnect attempt (after backoff delay).
|
|
92
|
+
*/
|
|
93
|
+
onReconnect?: (options: {
|
|
94
|
+
attempt: number;
|
|
95
|
+
cause: unknown;
|
|
96
|
+
}) => void;
|
|
97
|
+
/**
|
|
98
|
+
* Invoked after the socket has been re-established. Use to restore
|
|
99
|
+
* server-side subscription state (see `ThreadStream`).
|
|
100
|
+
*/
|
|
101
|
+
onReconnected?: () => void | Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Backoff before each reconnect attempt. Defaults to
|
|
104
|
+
* {@link webSocketReconnectDelayMs}.
|
|
105
|
+
*/
|
|
106
|
+
reconnectDelayMs?: (attempt: number) => number;
|
|
27
107
|
}
|
|
28
108
|
type HeaderValue = string | undefined | null;
|
|
29
109
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/client/stream/transport/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/client/stream/transport/types.ts"],"mappings":";;;;;KAIY,mBAAA,IACV,GAAA,EAAK,GAAA,EACL,IAAA,EAAM,WAAA,KACH,OAAA,CAAQ,WAAA,IAAe,WAAA;;AAH5B;;;;;;KAYY,YAAA,cAA0B,QAAA;AAAA,UAErB,sBAAA;EACf,QAAA,GAAW,YAAA;EACX,MAAA,GAAS,YAAA;EAfJ;EAiBL,KAAA,GAAQ,YAAA;AAAA;AAAA,UAGO,2BAAA;EACf,MAAA;EAnBW;;;;AASb;;EAiBE,QAAA;EACA,cAAA,GAAiB,MAAA,SAAe,WAAA;EAChC,SAAA,GAAY,mBAAA;EACZ,KAAA,UAAe,KAAA;EACf,YAAA,gBAA4B,KAAA,GAAQ,OAAA,QAAe,KAAA;;;;;;EAMnD,WAAA,GAAc,WAAA;EACd,KAAA,GAAQ,sBAAA;EAzBG;;;;EA8BX,oBAAA;EA3BoB;;AAGtB;;;;;;;;;;;;;;EAyCE,aAAA,GAAgB,iBAAA;EAxChB;EA0CA,WAAA,IAAe,OAAA;IAAW,OAAA;IAAiB,KAAA;EAAA;EAjC3C;;;;EAsCA,gBAAA,IAAoB,OAAA;AAAA;AAAA,UAGL,iCAAA;EACf,MAAA;EAlCA;;;;EAuCA,QAAA;EACA,cAAA,GAAiB,MAAA,SAAe,WAAA;EAChC,SAAA,GAAY,mBAAA;EACZ,gBAAA,IAAoB,GAAA,aAAgB,SAAA;EACpC,KAAA,GAAQ,IAAA,CAAK,sBAAA;EAlB8B;;;;EAuB3C,oBAAA;EAlBmC;AAGrC;;EAmBE,WAAA,IAAe,OAAA;IAAW,OAAA;IAAiB,KAAA;EAAA;EAVP;;;;EAepC,aAAA,gBAA6B,OAAA;EAAO;;;;EAKpC,gBAAA,IAAoB,OAAA;AAAA;AAAA,KAGV,WAAA"}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
//#region src/client/stream/transport/utils.ts
|
|
2
2
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a {@link ProtocolPath} against the transport's currently-bound
|
|
5
|
+
* thread.
|
|
6
|
+
*
|
|
7
|
+
* - a fixed `string` is used verbatim (back-compat: a baked path is
|
|
8
|
+
* independent of the bound thread);
|
|
9
|
+
* - a function path and the default fallback are evaluated against
|
|
10
|
+
* `threadId`, so late-bound / re-bound adapters target the right thread.
|
|
11
|
+
*
|
|
12
|
+
* Throws when neither a fixed path nor a bound thread is available — i.e.
|
|
13
|
+
* a request was attempted before `client.threads.stream(threadId, …)` /
|
|
14
|
+
* {@link TransportAdapter.setThreadId} bound a thread.
|
|
15
|
+
*/
|
|
16
|
+
function resolveProtocolPath(path, threadId, fallback) {
|
|
17
|
+
if (typeof path === "string") return path;
|
|
18
|
+
if (!threadId) throw new Error("Protocol transport has no bound threadId. Bind one — the framework calls client.threads.stream(threadId, { transport }) / transport.setThreadId(threadId) — before issuing requests.");
|
|
19
|
+
return path ? path(threadId) : fallback(threadId);
|
|
20
|
+
}
|
|
3
21
|
/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */
|
|
4
22
|
const toAbsoluteUrl = (apiUrl, path) => new URL(`${apiUrl.replace(/\/$/, "")}${path}`);
|
|
5
23
|
const toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
@@ -39,6 +57,7 @@ exports.hasHeaders = hasHeaders;
|
|
|
39
57
|
exports.isProtocolResponse = isProtocolResponse;
|
|
40
58
|
exports.isRecord = isRecord;
|
|
41
59
|
exports.mergeHeaders = mergeHeaders;
|
|
60
|
+
exports.resolveProtocolPath = resolveProtocolPath;
|
|
42
61
|
exports.toAbsoluteUrl = toAbsoluteUrl;
|
|
43
62
|
exports.toError = toError;
|
|
44
63
|
exports.toWebSocketUrl = toWebSocketUrl;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.cjs","names":[],"sources":["../../../../src/client/stream/transport/utils.ts"],"sourcesContent":["import type { HeaderValue } from \"./types.js\";\nimport type { CommandResponse, ErrorResponse } from \"@langchain/protocol\";\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */\nexport const toAbsoluteUrl = (apiUrl: string, path: string) =>\n new URL(`${apiUrl.replace(/\\/$/, \"\")}${path}`);\n\nexport const toError = (error: unknown) =>\n // oxlint-disable-next-line no-instanceof/no-instanceof\n error instanceof Error ? error : new Error(String(error));\n\nexport const toWebSocketUrl = (apiUrl: string): string => {\n // Extract path from the input (e.g. \"http://host/threads/X/stream\") and\n // swap the scheme to ws/wss. The caller passes a fully-formed URL\n // with the desired path.\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n};\n\nexport const hasHeaders = (headers?: Record<string, HeaderValue>) =>\n Object.values(headers ?? {}).some((value) => value != null);\n\nexport function mergeHeaders(\n ...headerGroups: Array<\n HeadersInit | Record<string, HeaderValue> | undefined | null\n >\n): Headers {\n const merged = new Headers();\n\n for (const group of headerGroups) {\n if (!group) {\n continue;\n }\n\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (group instanceof Headers) {\n group.forEach((value, key) => {\n merged.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(group)) {\n for (const [key, value] of group) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(group)) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n }\n\n return merged;\n}\n\nexport function isProtocolResponse(\n value: unknown\n): value is CommandResponse | ErrorResponse {\n return (\n isRecord(value) &&\n typeof value.type === \"string\" &&\n (value.type === \"success\" || value.type === \"error\")\n );\n}\n"],"mappings":";AAGA,MAAa,YAAY,UACvB,OAAO,UAAU,YAAY,UAAU
|
|
1
|
+
{"version":3,"file":"utils.cjs","names":[],"sources":["../../../../src/client/stream/transport/utils.ts"],"sourcesContent":["import type { HeaderValue, ProtocolPath } from \"./types.js\";\nimport type { CommandResponse, ErrorResponse } from \"@langchain/protocol\";\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/**\n * Resolve a {@link ProtocolPath} against the transport's currently-bound\n * thread.\n *\n * - a fixed `string` is used verbatim (back-compat: a baked path is\n * independent of the bound thread);\n * - a function path and the default fallback are evaluated against\n * `threadId`, so late-bound / re-bound adapters target the right thread.\n *\n * Throws when neither a fixed path nor a bound thread is available — i.e.\n * a request was attempted before `client.threads.stream(threadId, …)` /\n * {@link TransportAdapter.setThreadId} bound a thread.\n */\nexport function resolveProtocolPath(\n path: ProtocolPath | undefined,\n threadId: string,\n fallback: (threadId: string) => string\n): string {\n if (typeof path === \"string\") return path;\n if (!threadId) {\n throw new Error(\n \"Protocol transport has no bound threadId. Bind one — the framework \" +\n \"calls client.threads.stream(threadId, { transport }) / \" +\n \"transport.setThreadId(threadId) — before issuing requests.\"\n );\n }\n return path ? path(threadId) : fallback(threadId);\n}\n\n/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */\nexport const toAbsoluteUrl = (apiUrl: string, path: string) =>\n new URL(`${apiUrl.replace(/\\/$/, \"\")}${path}`);\n\nexport const toError = (error: unknown) =>\n // oxlint-disable-next-line no-instanceof/no-instanceof\n error instanceof Error ? error : new Error(String(error));\n\nexport const toWebSocketUrl = (apiUrl: string): string => {\n // Extract path from the input (e.g. \"http://host/threads/X/stream\") and\n // swap the scheme to ws/wss. The caller passes a fully-formed URL\n // with the desired path.\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n};\n\nexport const hasHeaders = (headers?: Record<string, HeaderValue>) =>\n Object.values(headers ?? {}).some((value) => value != null);\n\nexport function mergeHeaders(\n ...headerGroups: Array<\n HeadersInit | Record<string, HeaderValue> | undefined | null\n >\n): Headers {\n const merged = new Headers();\n\n for (const group of headerGroups) {\n if (!group) {\n continue;\n }\n\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (group instanceof Headers) {\n group.forEach((value, key) => {\n merged.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(group)) {\n for (const [key, value] of group) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(group)) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n }\n\n return merged;\n}\n\nexport function isProtocolResponse(\n value: unknown\n): value is CommandResponse | ErrorResponse {\n return (\n isRecord(value) &&\n typeof value.type === \"string\" &&\n (value.type === \"success\" || value.type === \"error\")\n );\n}\n"],"mappings":";AAGA,MAAa,YAAY,UACvB,OAAO,UAAU,YAAY,UAAU;;;;;;;;;;;;;;AAezC,SAAgB,oBACd,MACA,UACA,UACQ;AACR,KAAI,OAAO,SAAS,SAAU,QAAO;AACrC,KAAI,CAAC,SACH,OAAM,IAAI,MACR,uLAGD;AAEH,QAAO,OAAO,KAAK,SAAS,GAAG,SAAS,SAAS;;;AAInD,MAAa,iBAAiB,QAAgB,SAC5C,IAAI,IAAI,GAAG,OAAO,QAAQ,OAAO,GAAG,GAAG,OAAO;AAEhD,MAAa,WAAW,UAEtB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAE3D,MAAa,kBAAkB,WAA2B;CAIxD,MAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,KAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AACpD,KAAI,SAAS;AACb,KAAI,OAAO;AACX,QAAO,IAAI,UAAU;;AAGvB,MAAa,cAAc,YACzB,OAAO,OAAO,WAAW,EAAE,CAAC,CAAC,MAAM,UAAU,SAAS,KAAK;AAE7D,SAAgB,aACd,GAAG,cAGM;CACT,MAAM,SAAS,IAAI,SAAS;AAE5B,MAAK,MAAM,SAAS,cAAc;AAChC,MAAI,CAAC,MACH;AAIF,MAAI,iBAAiB,SAAS;AAC5B,SAAM,SAAS,OAAO,QAAQ;AAC5B,WAAO,IAAI,KAAK,MAAM;KACtB;AACF;;AAGF,MAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,QAAK,MAAM,CAAC,KAAK,UAAU,MACzB,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;OAElB,QAAO,IAAI,KAAK,MAAM;AAG1B;;AAGF,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;MAElB,QAAO,IAAI,KAAK,MAAM;;AAK5B,QAAO;;AAGT,SAAgB,mBACd,OAC0C;AAC1C,QACE,SAAS,MAAM,IACf,OAAO,MAAM,SAAS,aACrB,MAAM,SAAS,aAAa,MAAM,SAAS"}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
//#region src/client/stream/transport/utils.ts
|
|
2
2
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a {@link ProtocolPath} against the transport's currently-bound
|
|
5
|
+
* thread.
|
|
6
|
+
*
|
|
7
|
+
* - a fixed `string` is used verbatim (back-compat: a baked path is
|
|
8
|
+
* independent of the bound thread);
|
|
9
|
+
* - a function path and the default fallback are evaluated against
|
|
10
|
+
* `threadId`, so late-bound / re-bound adapters target the right thread.
|
|
11
|
+
*
|
|
12
|
+
* Throws when neither a fixed path nor a bound thread is available — i.e.
|
|
13
|
+
* a request was attempted before `client.threads.stream(threadId, …)` /
|
|
14
|
+
* {@link TransportAdapter.setThreadId} bound a thread.
|
|
15
|
+
*/
|
|
16
|
+
function resolveProtocolPath(path, threadId, fallback) {
|
|
17
|
+
if (typeof path === "string") return path;
|
|
18
|
+
if (!threadId) throw new Error("Protocol transport has no bound threadId. Bind one — the framework calls client.threads.stream(threadId, { transport }) / transport.setThreadId(threadId) — before issuing requests.");
|
|
19
|
+
return path ? path(threadId) : fallback(threadId);
|
|
20
|
+
}
|
|
3
21
|
/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */
|
|
4
22
|
const toAbsoluteUrl = (apiUrl, path) => new URL(`${apiUrl.replace(/\/$/, "")}${path}`);
|
|
5
23
|
const toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
@@ -35,6 +53,6 @@ function isProtocolResponse(value) {
|
|
|
35
53
|
return isRecord(value) && typeof value.type === "string" && (value.type === "success" || value.type === "error");
|
|
36
54
|
}
|
|
37
55
|
//#endregion
|
|
38
|
-
export { hasHeaders, isProtocolResponse, isRecord, mergeHeaders, toAbsoluteUrl, toError, toWebSocketUrl };
|
|
56
|
+
export { hasHeaders, isProtocolResponse, isRecord, mergeHeaders, resolveProtocolPath, toAbsoluteUrl, toError, toWebSocketUrl };
|
|
39
57
|
|
|
40
58
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../../../src/client/stream/transport/utils.ts"],"sourcesContent":["import type { HeaderValue } from \"./types.js\";\nimport type { CommandResponse, ErrorResponse } from \"@langchain/protocol\";\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */\nexport const toAbsoluteUrl = (apiUrl: string, path: string) =>\n new URL(`${apiUrl.replace(/\\/$/, \"\")}${path}`);\n\nexport const toError = (error: unknown) =>\n // oxlint-disable-next-line no-instanceof/no-instanceof\n error instanceof Error ? error : new Error(String(error));\n\nexport const toWebSocketUrl = (apiUrl: string): string => {\n // Extract path from the input (e.g. \"http://host/threads/X/stream\") and\n // swap the scheme to ws/wss. The caller passes a fully-formed URL\n // with the desired path.\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n};\n\nexport const hasHeaders = (headers?: Record<string, HeaderValue>) =>\n Object.values(headers ?? {}).some((value) => value != null);\n\nexport function mergeHeaders(\n ...headerGroups: Array<\n HeadersInit | Record<string, HeaderValue> | undefined | null\n >\n): Headers {\n const merged = new Headers();\n\n for (const group of headerGroups) {\n if (!group) {\n continue;\n }\n\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (group instanceof Headers) {\n group.forEach((value, key) => {\n merged.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(group)) {\n for (const [key, value] of group) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(group)) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n }\n\n return merged;\n}\n\nexport function isProtocolResponse(\n value: unknown\n): value is CommandResponse | ErrorResponse {\n return (\n isRecord(value) &&\n typeof value.type === \"string\" &&\n (value.type === \"success\" || value.type === \"error\")\n );\n}\n"],"mappings":";AAGA,MAAa,YAAY,UACvB,OAAO,UAAU,YAAY,UAAU
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../../../src/client/stream/transport/utils.ts"],"sourcesContent":["import type { HeaderValue, ProtocolPath } from \"./types.js\";\nimport type { CommandResponse, ErrorResponse } from \"@langchain/protocol\";\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/**\n * Resolve a {@link ProtocolPath} against the transport's currently-bound\n * thread.\n *\n * - a fixed `string` is used verbatim (back-compat: a baked path is\n * independent of the bound thread);\n * - a function path and the default fallback are evaluated against\n * `threadId`, so late-bound / re-bound adapters target the right thread.\n *\n * Throws when neither a fixed path nor a bound thread is available — i.e.\n * a request was attempted before `client.threads.stream(threadId, …)` /\n * {@link TransportAdapter.setThreadId} bound a thread.\n */\nexport function resolveProtocolPath(\n path: ProtocolPath | undefined,\n threadId: string,\n fallback: (threadId: string) => string\n): string {\n if (typeof path === \"string\") return path;\n if (!threadId) {\n throw new Error(\n \"Protocol transport has no bound threadId. Bind one — the framework \" +\n \"calls client.threads.stream(threadId, { transport }) / \" +\n \"transport.setThreadId(threadId) — before issuing requests.\"\n );\n }\n return path ? path(threadId) : fallback(threadId);\n}\n\n/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */\nexport const toAbsoluteUrl = (apiUrl: string, path: string) =>\n new URL(`${apiUrl.replace(/\\/$/, \"\")}${path}`);\n\nexport const toError = (error: unknown) =>\n // oxlint-disable-next-line no-instanceof/no-instanceof\n error instanceof Error ? error : new Error(String(error));\n\nexport const toWebSocketUrl = (apiUrl: string): string => {\n // Extract path from the input (e.g. \"http://host/threads/X/stream\") and\n // swap the scheme to ws/wss. The caller passes a fully-formed URL\n // with the desired path.\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n};\n\nexport const hasHeaders = (headers?: Record<string, HeaderValue>) =>\n Object.values(headers ?? {}).some((value) => value != null);\n\nexport function mergeHeaders(\n ...headerGroups: Array<\n HeadersInit | Record<string, HeaderValue> | undefined | null\n >\n): Headers {\n const merged = new Headers();\n\n for (const group of headerGroups) {\n if (!group) {\n continue;\n }\n\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (group instanceof Headers) {\n group.forEach((value, key) => {\n merged.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(group)) {\n for (const [key, value] of group) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(group)) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n }\n\n return merged;\n}\n\nexport function isProtocolResponse(\n value: unknown\n): value is CommandResponse | ErrorResponse {\n return (\n isRecord(value) &&\n typeof value.type === \"string\" &&\n (value.type === \"success\" || value.type === \"error\")\n );\n}\n"],"mappings":";AAGA,MAAa,YAAY,UACvB,OAAO,UAAU,YAAY,UAAU;;;;;;;;;;;;;;AAezC,SAAgB,oBACd,MACA,UACA,UACQ;AACR,KAAI,OAAO,SAAS,SAAU,QAAO;AACrC,KAAI,CAAC,SACH,OAAM,IAAI,MACR,uLAGD;AAEH,QAAO,OAAO,KAAK,SAAS,GAAG,SAAS,SAAS;;;AAInD,MAAa,iBAAiB,QAAgB,SAC5C,IAAI,IAAI,GAAG,OAAO,QAAQ,OAAO,GAAG,GAAG,OAAO;AAEhD,MAAa,WAAW,UAEtB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAE3D,MAAa,kBAAkB,WAA2B;CAIxD,MAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,KAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AACpD,KAAI,SAAS;AACb,KAAI,OAAO;AACX,QAAO,IAAI,UAAU;;AAGvB,MAAa,cAAc,YACzB,OAAO,OAAO,WAAW,EAAE,CAAC,CAAC,MAAM,UAAU,SAAS,KAAK;AAE7D,SAAgB,aACd,GAAG,cAGM;CACT,MAAM,SAAS,IAAI,SAAS;AAE5B,MAAK,MAAM,SAAS,cAAc;AAChC,MAAI,CAAC,MACH;AAIF,MAAI,iBAAiB,SAAS;AAC5B,SAAM,SAAS,OAAO,QAAQ;AAC5B,WAAO,IAAI,KAAK,MAAM;KACtB;AACF;;AAGF,MAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,QAAK,MAAM,CAAC,KAAK,UAAU,MACzB,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;OAElB,QAAO,IAAI,KAAK,MAAM;AAG1B;;AAGF,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;MAElB,QAAO,IAAI,KAAK,MAAM;;AAK5B,QAAO;;AAGT,SAAgB,mBACd,OAC0C;AAC1C,QACE,SAAS,MAAM,IACf,OAAO,MAAM,SAAS,aACrB,MAAM,SAAS,aAAa,MAAM,SAAS"}
|