@codehz/ai 0.2.3 → 0.2.4
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 +32 -31
- package/dist/index.d.mts +6 -3
- package/dist/index.mjs +30 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/chat-completions.ts +1 -0
- package/src/adapters/messages.ts +1 -0
- package/src/adapters/mock.ts +8 -2
- package/src/adapters/ollama.ts +1 -0
- package/src/adapters/responses.ts +1 -0
- package/src/core/client.ts +15 -2
- package/src/helpers/adapter-base.ts +3 -0
- package/src/index.ts +1 -1
- package/src/types/adapter.ts +2 -0
- package/src/types/request.ts +2 -0
package/package.json
CHANGED
|
@@ -398,6 +398,7 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
398
398
|
Authorization: `Bearer ${this.apiKey}`,
|
|
399
399
|
},
|
|
400
400
|
body: JSON.stringify(providerRequest),
|
|
401
|
+
signal: request.signal,
|
|
401
402
|
});
|
|
402
403
|
} catch (err) {
|
|
403
404
|
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
package/src/adapters/messages.ts
CHANGED
|
@@ -422,6 +422,7 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
422
422
|
"anthropic-version": this.apiVersion,
|
|
423
423
|
},
|
|
424
424
|
body: JSON.stringify(providerRequest),
|
|
425
|
+
signal: request.signal,
|
|
425
426
|
});
|
|
426
427
|
} catch (err) {
|
|
427
428
|
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
package/src/adapters/mock.ts
CHANGED
|
@@ -68,6 +68,8 @@ export type MockHandlerContext = {
|
|
|
68
68
|
previousReplay: ReplayItem[];
|
|
69
69
|
pendingToolCalls: readonly ToolCallItem[];
|
|
70
70
|
history: readonly MockHistoryRecord[];
|
|
71
|
+
/** 请求的 AbortSignal,handler 可检查 signal.aborted 提前退出。 */
|
|
72
|
+
signal?: AbortSignal;
|
|
71
73
|
};
|
|
72
74
|
|
|
73
75
|
export type MockWarningStep = {
|
|
@@ -290,7 +292,7 @@ export class MockAdapter extends AdapterBase {
|
|
|
290
292
|
|
|
291
293
|
protected async buildRequest(request: NormalizedRequest): Promise<MockProviderRequest> {
|
|
292
294
|
const turnIndex = this.cursor;
|
|
293
|
-
const context = this.buildHandlerContext(turnIndex);
|
|
295
|
+
const context = this.buildHandlerContext(turnIndex, request.signal);
|
|
294
296
|
const remainingPendingToolCalls = consumePendingToolCalls(this.pendingToolCalls, request.input);
|
|
295
297
|
const handlerResult = this.handler(request, context);
|
|
296
298
|
|
|
@@ -321,6 +323,9 @@ export class MockAdapter extends AdapterBase {
|
|
|
321
323
|
let stepCount = 0;
|
|
322
324
|
|
|
323
325
|
for await (const step of mockRequest.handlerResult) {
|
|
326
|
+
// 若 signal 已 abort,停止消费 handler 并返回
|
|
327
|
+
if (request.signal?.aborted) return;
|
|
328
|
+
|
|
324
329
|
stepCount += 1;
|
|
325
330
|
|
|
326
331
|
switch (step.type) {
|
|
@@ -479,7 +484,7 @@ export class MockAdapter extends AdapterBase {
|
|
|
479
484
|
);
|
|
480
485
|
}
|
|
481
486
|
|
|
482
|
-
private buildHandlerContext(turnIndex: number): MockHandlerContext {
|
|
487
|
+
private buildHandlerContext(turnIndex: number, signal?: AbortSignal): MockHandlerContext {
|
|
483
488
|
return {
|
|
484
489
|
turnIndex,
|
|
485
490
|
previousReplay: this.previousReplay.map(cloneItem),
|
|
@@ -489,6 +494,7 @@ export class MockAdapter extends AdapterBase {
|
|
|
489
494
|
replay: record.replay.map(cloneItem),
|
|
490
495
|
toolCalls: record.toolCalls.map(cloneItem),
|
|
491
496
|
})),
|
|
497
|
+
signal,
|
|
492
498
|
};
|
|
493
499
|
}
|
|
494
500
|
}
|
package/src/adapters/ollama.ts
CHANGED
|
@@ -351,6 +351,7 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
351
351
|
method: "POST",
|
|
352
352
|
headers,
|
|
353
353
|
body: JSON.stringify(providerRequest),
|
|
354
|
+
signal: request.signal,
|
|
354
355
|
});
|
|
355
356
|
} catch (err) {
|
|
356
357
|
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
@@ -330,6 +330,7 @@ export class ResponsesAdapter extends AdapterBase {
|
|
|
330
330
|
Authorization: `Bearer ${this.apiKey}`,
|
|
331
331
|
},
|
|
332
332
|
body: JSON.stringify(providerRequest),
|
|
333
|
+
signal: request.signal,
|
|
333
334
|
});
|
|
334
335
|
} catch (err) {
|
|
335
336
|
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
package/src/core/client.ts
CHANGED
|
@@ -8,11 +8,13 @@ import type { AIRequest, AIStreamEvent, AIClient, CreateAIClientOptions } from "
|
|
|
8
8
|
import { normalizeRequest } from "./normalize.js";
|
|
9
9
|
|
|
10
10
|
export function createAIClient(options: CreateAIClientOptions): AIClient {
|
|
11
|
-
const { adapter, model, defaults } = options;
|
|
11
|
+
const { adapter, model, defaults, signal: defaultSignal } = options;
|
|
12
12
|
|
|
13
13
|
const client: AIClient = {
|
|
14
14
|
stream(request: AIRequest): AsyncIterable<AIStreamEvent> {
|
|
15
|
-
|
|
15
|
+
// 合并 client 级别的默认 signal 和请求级别的 signal
|
|
16
|
+
const signal = mergeAbortSignals(defaultSignal, request.signal);
|
|
17
|
+
const normalized = normalizeRequest({ ...request, signal }, { model, defaults });
|
|
16
18
|
return adapter.stream(normalized);
|
|
17
19
|
},
|
|
18
20
|
};
|
|
@@ -20,4 +22,15 @@ export function createAIClient(options: CreateAIClientOptions): AIClient {
|
|
|
20
22
|
return client;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* 合并多个 AbortSignal:任一 signal abort 即触发。
|
|
27
|
+
* 如果没有 signal 需要合并则返回 undefined。
|
|
28
|
+
*/
|
|
29
|
+
function mergeAbortSignals(...signals: (AbortSignal | undefined)[]): AbortSignal | undefined {
|
|
30
|
+
const valid = signals.filter((s): s is AbortSignal => s != null);
|
|
31
|
+
if (valid.length === 0) return undefined;
|
|
32
|
+
if (valid.length === 1) return valid[0];
|
|
33
|
+
return AbortSignal.any(valid);
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
export type { AIClient, CreateAIClientOptions } from "../types/index.js";
|
|
@@ -65,6 +65,9 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
65
65
|
* 3. 委托 runStream 发射全部流事件(含 response.completed)
|
|
66
66
|
*/
|
|
67
67
|
async *stream(request: NormalizedRequest): AsyncIterable<AIStreamEvent> {
|
|
68
|
+
// 若请求已被 abort,不发出任何事件
|
|
69
|
+
request.signal?.throwIfAborted();
|
|
70
|
+
|
|
68
71
|
const factory = createEventFactory({
|
|
69
72
|
responseId: request.requestId,
|
|
70
73
|
backend: { kind: this.kind, isSynthetic: this.capabilities.textStreaming === "synthetic" },
|
package/src/index.ts
CHANGED
package/src/types/adapter.ts
CHANGED