@codehz/ai 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/index.d.mts +153 -31
- package/dist/index.mjs +1290 -727
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/chat-completions.ts +243 -196
- package/src/adapters/messages.ts +150 -124
- package/src/adapters/mock.ts +44 -11
- package/src/adapters/ollama.ts +219 -191
- package/src/adapters/responses.ts +222 -137
- package/src/core/aggregator.ts +218 -61
- package/src/core/errors.ts +7 -1
- package/src/core/event-factory.ts +24 -14
- package/src/core/merge-auxiliary.ts +22 -0
- package/src/core/normalize.ts +15 -1
- package/src/core/validation.ts +29 -21
- package/src/helpers/adapter-base.ts +23 -25
- package/src/helpers/adapter-security.ts +126 -0
- package/src/helpers/incremental-stream-parser.ts +84 -0
- package/src/helpers/index.ts +19 -0
- package/src/helpers/request-mapper.ts +72 -0
- package/src/helpers/sse-parser.ts +51 -25
- package/src/helpers/synthetic-stream.ts +13 -21
- package/src/helpers/usage-mapping.ts +6 -43
- package/src/types/adapter.ts +12 -1
- package/src/types/events.ts +14 -10
- package/src/types/index.ts +9 -1
- package/src/types/response.ts +0 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider usage → canonical Usage 映射
|
|
3
3
|
*
|
|
4
|
-
* best-effort 提取 reasoning / cache
|
|
4
|
+
* best-effort 提取 reasoning / cache 等扩展字段。
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { Usage } from "../types/index.js";
|
|
@@ -20,27 +20,6 @@ function record(obj: Record<string, number | undefined>): Partial<Usage> {
|
|
|
20
20
|
return out;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function billableFromOpenAIStyle(
|
|
24
|
-
inputTokens: number | undefined,
|
|
25
|
-
outputTokens: number | undefined,
|
|
26
|
-
cachedInputTokens: number | undefined,
|
|
27
|
-
reasoningTokens: number | undefined,
|
|
28
|
-
): Pick<Usage, "billableInputTokens" | "billableOutputTokens"> {
|
|
29
|
-
let billableInputTokens: number | undefined;
|
|
30
|
-
if (inputTokens !== undefined) {
|
|
31
|
-
billableInputTokens =
|
|
32
|
-
cachedInputTokens !== undefined ? Math.max(0, inputTokens - cachedInputTokens) : inputTokens;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let billableOutputTokens: number | undefined;
|
|
36
|
-
if (outputTokens !== undefined) {
|
|
37
|
-
billableOutputTokens =
|
|
38
|
-
reasoningTokens !== undefined ? Math.max(0, outputTokens - reasoningTokens) : outputTokens;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return record({ billableInputTokens, billableOutputTokens });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
23
|
/** OpenAI Chat Completions `usage` */
|
|
45
24
|
export function usageFromChatCompletions(raw: {
|
|
46
25
|
prompt_tokens?: number;
|
|
@@ -63,7 +42,6 @@ export function usageFromChatCompletions(raw: {
|
|
|
63
42
|
totalTokens,
|
|
64
43
|
cachedInputTokens,
|
|
65
44
|
reasoningTokens,
|
|
66
|
-
...billableFromOpenAIStyle(inputTokens, outputTokens, cachedInputTokens, reasoningTokens),
|
|
67
45
|
});
|
|
68
46
|
}
|
|
69
47
|
|
|
@@ -90,7 +68,6 @@ export function usageFromOpenAIResponses(raw: {
|
|
|
90
68
|
totalTokens,
|
|
91
69
|
cachedInputTokens,
|
|
92
70
|
reasoningTokens,
|
|
93
|
-
...billableFromOpenAIStyle(inputTokens, outputTokens, cachedInputTokens, reasoningTokens),
|
|
94
71
|
});
|
|
95
72
|
}
|
|
96
73
|
|
|
@@ -111,13 +88,7 @@ export function usageFromAnthropicMessages(raw: {
|
|
|
111
88
|
(n): n is number => n !== undefined,
|
|
112
89
|
);
|
|
113
90
|
const inputTokens = inputParts.length > 0 ? inputParts.reduce((sum, n) => sum + n, 0) : undefined;
|
|
114
|
-
const totalTokens =
|
|
115
|
-
inputTokens !== undefined && outputTokens !== undefined ? inputTokens + outputTokens : undefined;
|
|
116
|
-
|
|
117
|
-
let billableInputTokens: number | undefined;
|
|
118
|
-
if (uncachedInputTokens !== undefined || cacheWriteInputTokens !== undefined) {
|
|
119
|
-
billableInputTokens = (uncachedInputTokens ?? 0) + (cacheWriteInputTokens ?? 0);
|
|
120
|
-
}
|
|
91
|
+
const totalTokens = inputTokens !== undefined && outputTokens !== undefined ? inputTokens + outputTokens : undefined;
|
|
121
92
|
|
|
122
93
|
return record({
|
|
123
94
|
inputTokens,
|
|
@@ -125,26 +96,18 @@ export function usageFromAnthropicMessages(raw: {
|
|
|
125
96
|
totalTokens,
|
|
126
97
|
cachedInputTokens,
|
|
127
98
|
cacheWriteInputTokens,
|
|
128
|
-
billableInputTokens,
|
|
129
|
-
billableOutputTokens: outputTokens,
|
|
130
99
|
});
|
|
131
100
|
}
|
|
132
101
|
|
|
133
|
-
/** Ollama 流式 chunk
|
|
134
|
-
export function usageFromOllama(raw: {
|
|
135
|
-
prompt_eval_count?: number;
|
|
136
|
-
eval_count?: number;
|
|
137
|
-
}): Partial<Usage> {
|
|
102
|
+
/** Ollama 流式 chunk */
|
|
103
|
+
export function usageFromOllama(raw: { prompt_eval_count?: number; eval_count?: number }): Partial<Usage> {
|
|
138
104
|
const inputTokens = num(raw.prompt_eval_count);
|
|
139
105
|
const outputTokens = num(raw.eval_count);
|
|
140
|
-
const totalTokens =
|
|
141
|
-
inputTokens !== undefined && outputTokens !== undefined ? inputTokens + outputTokens : undefined;
|
|
106
|
+
const totalTokens = inputTokens !== undefined && outputTokens !== undefined ? inputTokens + outputTokens : undefined;
|
|
142
107
|
|
|
143
108
|
return record({
|
|
144
109
|
inputTokens,
|
|
145
110
|
outputTokens,
|
|
146
111
|
totalTokens,
|
|
147
|
-
billableInputTokens: inputTokens,
|
|
148
|
-
billableOutputTokens: outputTokens,
|
|
149
112
|
});
|
|
150
|
-
}
|
|
113
|
+
}
|
package/src/types/adapter.ts
CHANGED
|
@@ -21,9 +21,20 @@ export type NormalizedRequest = AIRequest & {
|
|
|
21
21
|
|
|
22
22
|
// ── Adapter 接口 ──────────────────────────────────────────────
|
|
23
23
|
|
|
24
|
+
export type StreamingCapability = "native" | "synthetic" | "none";
|
|
25
|
+
|
|
26
|
+
export type AdapterCapabilities = {
|
|
27
|
+
readonly textStreaming: StreamingCapability;
|
|
28
|
+
readonly reasoningStreaming: StreamingCapability;
|
|
29
|
+
readonly toolCallStreaming: StreamingCapability;
|
|
30
|
+
readonly replay: "canonical" | "opaque" | "none";
|
|
31
|
+
readonly usage: "stream" | "final" | "none";
|
|
32
|
+
readonly toolResultOutcomes: ReadonlyArray<"success" | "error" | "rejected">;
|
|
33
|
+
};
|
|
34
|
+
|
|
24
35
|
export interface BackendAdapter {
|
|
25
36
|
readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
26
|
-
readonly
|
|
37
|
+
readonly capabilities: AdapterCapabilities;
|
|
27
38
|
stream(request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
|
|
28
39
|
}
|
|
29
40
|
|
package/src/types/events.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { ContentBlock } from "./content.js";
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
10
|
+
import type { Usage, BillingInfo, AuxiliaryInfo, BackendTrace, StopReason } from "./response.js";
|
|
11
|
+
import type { OpaqueItem, ReplayItem } from "./items.js";
|
|
12
12
|
|
|
13
13
|
// ── 事件基类 ──────────────────────────────────────────────────
|
|
14
14
|
|
|
@@ -45,7 +45,14 @@ export type ResponseAuxiliaryEvent = StreamEventBase & {
|
|
|
45
45
|
|
|
46
46
|
export type ResponseCompletedEvent = StreamEventBase & {
|
|
47
47
|
type: "response.completed";
|
|
48
|
-
|
|
48
|
+
replay: ReplayItem[];
|
|
49
|
+
stopReason?: StopReason;
|
|
50
|
+
usage?: Usage;
|
|
51
|
+
billing?: BillingInfo;
|
|
52
|
+
auxiliary?: AuxiliaryInfo;
|
|
53
|
+
warnings?: string[];
|
|
54
|
+
opaqueOutput?: OpaqueItem[];
|
|
55
|
+
trace?: Partial<BackendTrace>;
|
|
49
56
|
};
|
|
50
57
|
|
|
51
58
|
// ── 消息流事件 ────────────────────────────────────────────────
|
|
@@ -61,15 +68,12 @@ export type MessageStartedEvent = StreamEventBase & {
|
|
|
61
68
|
export type MessageDeltaEvent = StreamEventBase & {
|
|
62
69
|
type: "message.delta";
|
|
63
70
|
itemId: string;
|
|
64
|
-
delta:
|
|
65
|
-
type: "text";
|
|
66
|
-
text: string;
|
|
67
|
-
};
|
|
71
|
+
delta: ContentBlock;
|
|
68
72
|
};
|
|
69
73
|
|
|
70
74
|
export type MessageCompletedEvent = StreamEventBase & {
|
|
71
75
|
type: "message.completed";
|
|
72
|
-
|
|
76
|
+
itemId: string;
|
|
73
77
|
};
|
|
74
78
|
|
|
75
79
|
// ── 思维链流事件 ──────────────────────────────────────────────
|
|
@@ -90,7 +94,7 @@ export type ReasoningDeltaEvent = StreamEventBase & {
|
|
|
90
94
|
|
|
91
95
|
export type ReasoningCompletedEvent = StreamEventBase & {
|
|
92
96
|
type: "reasoning.completed";
|
|
93
|
-
|
|
97
|
+
itemId: string;
|
|
94
98
|
};
|
|
95
99
|
|
|
96
100
|
// ── 工具调用流事件 ────────────────────────────────────────────
|
|
@@ -113,7 +117,7 @@ export type ToolCallDeltaEvent = StreamEventBase & {
|
|
|
113
117
|
|
|
114
118
|
export type ToolCallCompletedEvent = StreamEventBase & {
|
|
115
119
|
type: "tool_call.completed";
|
|
116
|
-
|
|
120
|
+
itemId: string;
|
|
117
121
|
};
|
|
118
122
|
|
|
119
123
|
// ── 统一事件联合 ──────────────────────────────────────────────
|
package/src/types/index.ts
CHANGED
|
@@ -46,4 +46,12 @@ export type {
|
|
|
46
46
|
} from "./events.js";
|
|
47
47
|
|
|
48
48
|
// Adapter 协议和 client 类型
|
|
49
|
-
export type {
|
|
49
|
+
export type {
|
|
50
|
+
AdapterCapabilities,
|
|
51
|
+
StreamingCapability,
|
|
52
|
+
BackendAdapter,
|
|
53
|
+
FetchFn,
|
|
54
|
+
NormalizedRequest,
|
|
55
|
+
CreateAIClientOptions,
|
|
56
|
+
AIClient,
|
|
57
|
+
} from "./adapter.js";
|
package/src/types/response.ts
CHANGED
|
@@ -24,10 +24,6 @@ export type Usage = {
|
|
|
24
24
|
cachedInputTokens?: number;
|
|
25
25
|
/** Tokens written to prompt cache (Anthropic cache_creation_input_tokens) */
|
|
26
26
|
cacheWriteInputTokens?: number;
|
|
27
|
-
/** Best-effort billable input (full-rate input; excludes discounted cache reads where known) */
|
|
28
|
-
billableInputTokens?: number;
|
|
29
|
-
/** Best-effort billable output (non-reasoning slice when provider gives reasoning breakdown) */
|
|
30
|
-
billableOutputTokens?: number;
|
|
31
27
|
};
|
|
32
28
|
|
|
33
29
|
export type BillingInfo = {
|