@codehz/ai 0.1.2 → 0.1.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/.oxlintrc.json +1 -16
- package/AGENTS.md +37 -0
- package/README.md +102 -73
- package/dist/index.d.mts +44 -142
- package/dist/index.mjs +194 -219
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/adapters/chat-completions.ts +7 -29
- package/src/adapters/index.ts +6 -3
- package/src/adapters/messages.ts +21 -12
- package/src/adapters/mock.ts +332 -153
- package/src/adapters/ollama.ts +32 -33
- package/src/adapters/responses.ts +5 -8
- package/src/core/validation.ts +21 -16
- package/src/helpers/adapter-auxiliary.ts +4 -8
- package/src/helpers/adapter-base.ts +9 -10
- package/src/helpers/index.ts +1 -5
- package/src/types/adapter.ts +1 -82
- package/src/types/index.ts +1 -11
package/src/adapters/ollama.ts
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from "../helpers/mapping.js";
|
|
29
29
|
import { emitMalformedStreamWarning } from "../helpers/adapter-auxiliary.js";
|
|
30
30
|
|
|
31
|
-
import type {
|
|
31
|
+
import type { NormalizedRequest, AIStreamEvent, EventFactory, OutputItem, FetchFn } from "../index.js";
|
|
32
32
|
|
|
33
33
|
// ── 选项类型 ──────────────────────────────────────────────────
|
|
34
34
|
|
|
@@ -207,37 +207,29 @@ function rollbackTrailingAssistantMessages(messages: OllamaMessage[]): void {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
function isOllamaToolCalls(value: unknown): value is OllamaToolCall[] {
|
|
210
|
-
return
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
210
|
+
return (
|
|
211
|
+
Array.isArray(value) &&
|
|
212
|
+
value.every((entry) => {
|
|
213
|
+
if (!entry || typeof entry !== "object" || !("function" in entry)) return false;
|
|
214
|
+
const fn = (entry as { function?: unknown }).function;
|
|
215
|
+
return (
|
|
216
|
+
!!fn &&
|
|
217
|
+
typeof fn === "object" &&
|
|
218
|
+
"name" in fn &&
|
|
219
|
+
typeof (fn as { name?: unknown }).name === "string" &&
|
|
220
|
+
"arguments" in fn &&
|
|
221
|
+
typeof (fn as { arguments?: unknown }).arguments === "object" &&
|
|
222
|
+
(fn as { arguments?: unknown }).arguments !== null
|
|
223
|
+
);
|
|
224
|
+
})
|
|
225
|
+
);
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
// ── Adapter ───────────────────────────────────────────────────
|
|
226
229
|
|
|
227
230
|
export class OllamaAdapter extends AdapterBase {
|
|
228
231
|
readonly kind = "ollama" as const;
|
|
229
|
-
readonly
|
|
230
|
-
nativeStreaming: true,
|
|
231
|
-
messageStreaming: true,
|
|
232
|
-
reasoningStreaming: false,
|
|
233
|
-
toolCallStreaming: false,
|
|
234
|
-
hiddenReasoningReplay: "none" as const,
|
|
235
|
-
replayFidelity: "low" as const,
|
|
236
|
-
tools: true,
|
|
237
|
-
usage: "partial" as const,
|
|
238
|
-
billing: "none" as const,
|
|
239
|
-
providerMetadata: false,
|
|
240
|
-
};
|
|
232
|
+
readonly nativeStreaming = true;
|
|
241
233
|
|
|
242
234
|
private baseUrl: string;
|
|
243
235
|
private apiKey: string | undefined;
|
|
@@ -275,7 +267,10 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
275
267
|
: item.role === "user"
|
|
276
268
|
? "user"
|
|
277
269
|
: "assistant";
|
|
278
|
-
messages.push({
|
|
270
|
+
messages.push({
|
|
271
|
+
role,
|
|
272
|
+
content: contentBlocksToText(ensureOllamaTextBlocks(item.content, `input message (${item.role}) content`)),
|
|
273
|
+
});
|
|
279
274
|
break;
|
|
280
275
|
}
|
|
281
276
|
case "tool_call": {
|
|
@@ -312,7 +307,12 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
312
307
|
}
|
|
313
308
|
case "opaque": {
|
|
314
309
|
// Best-effort restore from opaque replay
|
|
315
|
-
if (
|
|
310
|
+
if (
|
|
311
|
+
item.source === "ollama" &&
|
|
312
|
+
item.purpose === "replay" &&
|
|
313
|
+
typeof item.payload === "object" &&
|
|
314
|
+
item.payload !== null
|
|
315
|
+
) {
|
|
316
316
|
const payload = item.payload as Record<string, unknown>;
|
|
317
317
|
if (payload.role === "assistant" && typeof payload.content === "string") {
|
|
318
318
|
rollbackTrailingAssistantMessages(messages);
|
|
@@ -406,7 +406,6 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
406
406
|
|
|
407
407
|
try {
|
|
408
408
|
while (true) {
|
|
409
|
-
// oxlint-disable-next-line no-await-in-loop
|
|
410
409
|
const { done, value } = await reader.read();
|
|
411
410
|
if (done) break;
|
|
412
411
|
|
|
@@ -489,9 +488,10 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
489
488
|
{
|
|
490
489
|
inputTokens: chunk.prompt_eval_count,
|
|
491
490
|
outputTokens: chunk.eval_count,
|
|
492
|
-
totalTokens:
|
|
493
|
-
|
|
494
|
-
|
|
491
|
+
totalTokens:
|
|
492
|
+
chunk.prompt_eval_count !== undefined && chunk.eval_count !== undefined
|
|
493
|
+
? chunk.prompt_eval_count + chunk.eval_count
|
|
494
|
+
: undefined,
|
|
495
495
|
},
|
|
496
496
|
"final",
|
|
497
497
|
{
|
|
@@ -520,7 +520,6 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
520
520
|
);
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
// oxlint-disable-next-line no-await-in-loop
|
|
524
523
|
const auxiliaryResult = await auxiliary.finalize(factory);
|
|
525
524
|
for (const event of auxiliaryResult.events) {
|
|
526
525
|
yield event;
|
|
@@ -25,7 +25,6 @@ import { emitMalformedStreamWarning } from "../helpers/adapter-auxiliary.js";
|
|
|
25
25
|
|
|
26
26
|
import { parseSSEEvents } from "../helpers/sse-parser.js";
|
|
27
27
|
|
|
28
|
-
import { CAPABILITY_MATRIX } from "../index.js";
|
|
29
28
|
import type { NormalizedRequest, AIStreamEvent, EventFactory, OutputItem, FetchFn } from "../index.js";
|
|
30
29
|
|
|
31
30
|
// ── 类型 ──────────────────────────────────────────────────────
|
|
@@ -165,9 +164,7 @@ function parseSSE(chunk: string): { events: ResponsesSSEEvent[]; rest: string; m
|
|
|
165
164
|
|
|
166
165
|
function isReplayCanonicalInput(item: ResponsesInputItem): boolean {
|
|
167
166
|
return (
|
|
168
|
-
(item.type === "message" && item.role === "assistant") ||
|
|
169
|
-
item.type === "reasoning" ||
|
|
170
|
-
item.type === "function_call"
|
|
167
|
+
(item.type === "message" && item.role === "assistant") || item.type === "reasoning" || item.type === "function_call"
|
|
171
168
|
);
|
|
172
169
|
}
|
|
173
170
|
|
|
@@ -194,7 +191,7 @@ function canonicalToResponsesBlock(b: import("../index.js").ContentBlock): Respo
|
|
|
194
191
|
|
|
195
192
|
export class ResponsesAdapter extends AdapterBase {
|
|
196
193
|
readonly kind = "responses" as const;
|
|
197
|
-
readonly
|
|
194
|
+
readonly nativeStreaming = true;
|
|
198
195
|
|
|
199
196
|
private apiKey: string;
|
|
200
197
|
private baseUrl: string;
|
|
@@ -453,9 +450,9 @@ export class ResponsesAdapter extends AdapterBase {
|
|
|
453
450
|
if (completedResponse.usage) {
|
|
454
451
|
auxiliary.recordUsage(
|
|
455
452
|
{
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
453
|
+
inputTokens: completedResponse.usage.input_tokens,
|
|
454
|
+
outputTokens: completedResponse.usage.output_tokens,
|
|
455
|
+
totalTokens: completedResponse.usage.total_tokens,
|
|
459
456
|
},
|
|
460
457
|
"final",
|
|
461
458
|
completedResponse.usage,
|
package/src/core/validation.ts
CHANGED
|
@@ -122,10 +122,20 @@ function validateInputItem(item: unknown, field: string, issues: ValidationIssue
|
|
|
122
122
|
return;
|
|
123
123
|
case "tool_result":
|
|
124
124
|
if (typeof item.callId !== "string" || item.callId.length === 0) {
|
|
125
|
-
pushIssue(
|
|
125
|
+
pushIssue(
|
|
126
|
+
issues,
|
|
127
|
+
`${field}.callId`,
|
|
128
|
+
"TOOL_RESULT_CALL_ID_INVALID",
|
|
129
|
+
`${field}.callId must be a non-empty string`,
|
|
130
|
+
);
|
|
126
131
|
}
|
|
127
132
|
if (typeof item.toolName !== "string" || item.toolName.length === 0) {
|
|
128
|
-
pushIssue(
|
|
133
|
+
pushIssue(
|
|
134
|
+
issues,
|
|
135
|
+
`${field}.toolName`,
|
|
136
|
+
"TOOL_RESULT_NAME_INVALID",
|
|
137
|
+
`${field}.toolName must be a non-empty string`,
|
|
138
|
+
);
|
|
129
139
|
}
|
|
130
140
|
if (typeof item.outcome !== "string" || !TOOL_RESULT_OUTCOMES.has(item.outcome)) {
|
|
131
141
|
pushIssue(
|
|
@@ -180,12 +190,7 @@ function validateTools(tools: unknown, issues: ValidationIssue[]): void {
|
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
if (!isRecord(tool.inputSchema)) {
|
|
183
|
-
pushIssue(
|
|
184
|
-
issues,
|
|
185
|
-
`${field}.inputSchema`,
|
|
186
|
-
"TOOL_INPUT_SCHEMA_INVALID",
|
|
187
|
-
`${field}.inputSchema must be an object`,
|
|
188
|
-
);
|
|
193
|
+
pushIssue(issues, `${field}.inputSchema`, "TOOL_INPUT_SCHEMA_INVALID", `${field}.inputSchema must be an object`);
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
}
|
|
@@ -193,8 +198,13 @@ function validateTools(tools: unknown, issues: ValidationIssue[]): void {
|
|
|
193
198
|
function validateToolChoice(toolChoice: unknown, issues: ValidationIssue[]): void {
|
|
194
199
|
if (toolChoice === undefined) return;
|
|
195
200
|
if (toolChoice === "auto" || toolChoice === "none") return;
|
|
196
|
-
if (
|
|
197
|
-
|
|
201
|
+
if (
|
|
202
|
+
!isRecord(toolChoice) ||
|
|
203
|
+
toolChoice.type !== "tool" ||
|
|
204
|
+
typeof toolChoice.name !== "string" ||
|
|
205
|
+
toolChoice.name.length === 0
|
|
206
|
+
) {
|
|
207
|
+
pushIssue(issues, "toolChoice", "TOOL_CHOICE_INVALID", 'toolChoice must be auto, none, or { type: "tool", name }');
|
|
198
208
|
}
|
|
199
209
|
}
|
|
200
210
|
|
|
@@ -211,12 +221,7 @@ export function validateRequest(request: AIRequest): ValidationIssue[] {
|
|
|
211
221
|
} else if (Array.isArray(request.instructions)) {
|
|
212
222
|
validateContentArray(request.instructions, "instructions", issues, "INSTRUCTIONS_INVALID");
|
|
213
223
|
} else {
|
|
214
|
-
pushIssue(
|
|
215
|
-
issues,
|
|
216
|
-
"instructions",
|
|
217
|
-
"INSTRUCTIONS_INVALID",
|
|
218
|
-
"instructions must be a string or ContentBlock[]",
|
|
219
|
-
);
|
|
224
|
+
pushIssue(issues, "instructions", "INSTRUCTIONS_INVALID", "instructions must be a string or ContentBlock[]");
|
|
220
225
|
}
|
|
221
226
|
}
|
|
222
227
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { WarningCode } from "../core/errors.js";
|
|
2
2
|
import type { EventFactory } from "../core/event-factory.js";
|
|
3
3
|
import type {
|
|
4
|
-
AdapterCapabilities,
|
|
5
4
|
AIStreamEvent,
|
|
6
5
|
BillingInfo,
|
|
7
6
|
NormalizedRequest,
|
|
@@ -18,7 +17,6 @@ export type BillingPostprocessHook = (context: {
|
|
|
18
17
|
usage?: Usage;
|
|
19
18
|
billing?: BillingInfo;
|
|
20
19
|
auxiliary?: AuxiliaryInfo;
|
|
21
|
-
capabilities: AdapterCapabilities;
|
|
22
20
|
}) => MaybePromise<Partial<BillingInfo> | undefined>;
|
|
23
21
|
|
|
24
22
|
export type AuxiliaryFinalizeOptions = {
|
|
@@ -41,10 +39,7 @@ export class AdapterAuxiliaryState {
|
|
|
41
39
|
private readonly collector = new AuxiliaryCollector();
|
|
42
40
|
private readonly metadataSources = new Set<string>();
|
|
43
41
|
|
|
44
|
-
constructor(
|
|
45
|
-
private readonly request: NormalizedRequest,
|
|
46
|
-
private readonly capabilities: AdapterCapabilities,
|
|
47
|
-
) {}
|
|
42
|
+
constructor(private readonly request: NormalizedRequest) {}
|
|
48
43
|
|
|
49
44
|
recordUsage(usage: Partial<Usage>, source: UsageSource, raw?: unknown): void {
|
|
50
45
|
if (this.request.include?.usage === "off" || isEmptyRecord(usage)) return;
|
|
@@ -75,7 +70,6 @@ export class AdapterAuxiliaryState {
|
|
|
75
70
|
usage: snapshot.usage,
|
|
76
71
|
billing: snapshot.billing,
|
|
77
72
|
auxiliary: snapshot.auxiliary,
|
|
78
|
-
capabilities: this.capabilities,
|
|
79
73
|
});
|
|
80
74
|
if (derived && !isEmptyRecord(derived)) {
|
|
81
75
|
this.collector.recordBilling(
|
|
@@ -105,7 +99,9 @@ export class AdapterAuxiliaryState {
|
|
|
105
99
|
}
|
|
106
100
|
|
|
107
101
|
if (this.request.include?.usage !== "off" && !built.usage) {
|
|
108
|
-
events.push(
|
|
102
|
+
events.push(
|
|
103
|
+
factory.responseWarning("Usage information was not provided by the provider", WarningCode.USAGE_MISSING),
|
|
104
|
+
);
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
if (this.request.include?.billing !== "off") {
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import type {
|
|
15
15
|
NormalizedRequest,
|
|
16
16
|
BackendAdapter,
|
|
17
|
-
AdapterCapabilities,
|
|
18
17
|
AIStreamEvent,
|
|
19
18
|
AIResponse,
|
|
20
19
|
AuxiliaryInfo,
|
|
@@ -56,7 +55,7 @@ export type StreamResult = {
|
|
|
56
55
|
|
|
57
56
|
export abstract class AdapterBase implements BackendAdapter {
|
|
58
57
|
abstract readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
59
|
-
abstract readonly
|
|
58
|
+
abstract readonly nativeStreaming: boolean;
|
|
60
59
|
|
|
61
60
|
/**
|
|
62
61
|
* stream 模板方法:
|
|
@@ -67,7 +66,7 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
67
66
|
async *stream(request: NormalizedRequest): AsyncIterable<AIStreamEvent> {
|
|
68
67
|
const factory = createEventFactory({
|
|
69
68
|
responseId: request.requestId,
|
|
70
|
-
backend: { kind: this.kind, isSynthetic: !this.
|
|
69
|
+
backend: { kind: this.kind, isSynthetic: !this.nativeStreaming },
|
|
71
70
|
});
|
|
72
71
|
|
|
73
72
|
yield factory.responseStarted(request.model);
|
|
@@ -113,7 +112,10 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
113
112
|
protected buildResponse(request: NormalizedRequest, result: StreamResult, _factory: EventFactory): AIResponse {
|
|
114
113
|
const text = this.extractText(result.output);
|
|
115
114
|
const warnings = mergeWarnings(result.warnings, _factory.warnings);
|
|
116
|
-
const auxiliary = mergeAuxiliary(
|
|
115
|
+
const auxiliary = mergeAuxiliary(
|
|
116
|
+
result.auxiliary,
|
|
117
|
+
result.providerMetadata ? { providerMetadata: result.providerMetadata } : undefined,
|
|
118
|
+
);
|
|
117
119
|
|
|
118
120
|
return {
|
|
119
121
|
id: request.requestId,
|
|
@@ -130,7 +132,7 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
130
132
|
requestId: request.requestId,
|
|
131
133
|
rawResponseId: result.rawResponseId,
|
|
132
134
|
adapter: this.kind,
|
|
133
|
-
isSyntheticStream: !this.
|
|
135
|
+
isSyntheticStream: !this.nativeStreaming,
|
|
134
136
|
metadataSources: result.metadataSources,
|
|
135
137
|
warnings,
|
|
136
138
|
},
|
|
@@ -143,14 +145,11 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
protected createAuxiliaryState(request: NormalizedRequest): AdapterAuxiliaryState {
|
|
146
|
-
return new AdapterAuxiliaryState(request
|
|
148
|
+
return new AdapterAuxiliaryState(request);
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
function mergeAuxiliary(
|
|
151
|
-
base?: Partial<AuxiliaryInfo>,
|
|
152
|
-
patch?: Partial<AuxiliaryInfo>,
|
|
153
|
-
): AuxiliaryInfo | undefined {
|
|
152
|
+
function mergeAuxiliary(base?: Partial<AuxiliaryInfo>, patch?: Partial<AuxiliaryInfo>): AuxiliaryInfo | undefined {
|
|
154
153
|
if (!base && !patch) return undefined;
|
|
155
154
|
|
|
156
155
|
const merged: AuxiliaryInfo = {
|
package/src/helpers/index.ts
CHANGED
|
@@ -28,11 +28,7 @@ export type { SSEEvent } from "./sse-parser.js";
|
|
|
28
28
|
|
|
29
29
|
export { AdapterBase } from "./adapter-base.js";
|
|
30
30
|
export type { StreamResult } from "./adapter-base.js";
|
|
31
|
-
export {
|
|
32
|
-
AdapterAuxiliaryState,
|
|
33
|
-
emitMalformedStreamWarning,
|
|
34
|
-
metadataSourceList,
|
|
35
|
-
} from "./adapter-auxiliary.js";
|
|
31
|
+
export { AdapterAuxiliaryState, emitMalformedStreamWarning, metadataSourceList } from "./adapter-auxiliary.js";
|
|
36
32
|
export type { AuxiliaryFinalizeOptions, AuxiliaryFinalizeResult, BillingPostprocessHook } from "./adapter-auxiliary.js";
|
|
37
33
|
export { syntheticStream } from "./synthetic-stream.js";
|
|
38
34
|
export type { SyntheticStreamOptions } from "./synthetic-stream.js";
|
package/src/types/adapter.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* BackendAdapter — adapter 内部协议和 client 公开类型
|
|
3
3
|
*
|
|
4
4
|
* adapter 对前台只暴露一个统一适配点。
|
|
5
|
-
* 能力矩阵在此落成代码而非仅存在于文档。
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import type { AIRequest } from "./request.js";
|
|
@@ -20,91 +19,11 @@ export type NormalizedRequest = AIRequest & {
|
|
|
20
19
|
requestId: string;
|
|
21
20
|
};
|
|
22
21
|
|
|
23
|
-
// ── 能力矩阵 ──────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
export type AdapterCapabilities = {
|
|
26
|
-
nativeStreaming: boolean;
|
|
27
|
-
messageStreaming: boolean;
|
|
28
|
-
reasoningStreaming: boolean;
|
|
29
|
-
toolCallStreaming: boolean;
|
|
30
|
-
hiddenReasoningReplay: "full" | "partial" | "none";
|
|
31
|
-
replayFidelity: "high" | "medium" | "low";
|
|
32
|
-
tools: boolean;
|
|
33
|
-
usage: "full" | "partial" | "none";
|
|
34
|
-
billing: "direct" | "lookup" | "derived" | "none";
|
|
35
|
-
providerMetadata: boolean;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// ── 能力矩阵常量(文档中的能力表在此落代码) ────────────────
|
|
39
|
-
|
|
40
|
-
export const CAPABILITY_MATRIX = {
|
|
41
|
-
responses: {
|
|
42
|
-
nativeStreaming: true,
|
|
43
|
-
messageStreaming: true,
|
|
44
|
-
reasoningStreaming: true,
|
|
45
|
-
toolCallStreaming: true,
|
|
46
|
-
hiddenReasoningReplay: "full" as const,
|
|
47
|
-
replayFidelity: "high" as const,
|
|
48
|
-
tools: true,
|
|
49
|
-
usage: "full" as const,
|
|
50
|
-
billing: "lookup" as const,
|
|
51
|
-
providerMetadata: true,
|
|
52
|
-
},
|
|
53
|
-
messages: {
|
|
54
|
-
nativeStreaming: true,
|
|
55
|
-
messageStreaming: true,
|
|
56
|
-
reasoningStreaming: false, // 条件支持,默认 false
|
|
57
|
-
toolCallStreaming: true,
|
|
58
|
-
hiddenReasoningReplay: "partial" as const,
|
|
59
|
-
replayFidelity: "medium" as const,
|
|
60
|
-
tools: true,
|
|
61
|
-
usage: "full" as const,
|
|
62
|
-
billing: "lookup" as const,
|
|
63
|
-
providerMetadata: true,
|
|
64
|
-
},
|
|
65
|
-
"chat.completions": {
|
|
66
|
-
nativeStreaming: true,
|
|
67
|
-
messageStreaming: true,
|
|
68
|
-
reasoningStreaming: false,
|
|
69
|
-
toolCallStreaming: false, // 中,默认 false
|
|
70
|
-
hiddenReasoningReplay: "none" as const,
|
|
71
|
-
replayFidelity: "low" as const,
|
|
72
|
-
tools: true,
|
|
73
|
-
usage: "full" as const,
|
|
74
|
-
billing: "derived" as const,
|
|
75
|
-
providerMetadata: false,
|
|
76
|
-
},
|
|
77
|
-
ollama: {
|
|
78
|
-
nativeStreaming: true,
|
|
79
|
-
messageStreaming: true,
|
|
80
|
-
reasoningStreaming: false,
|
|
81
|
-
toolCallStreaming: false,
|
|
82
|
-
hiddenReasoningReplay: "none" as const,
|
|
83
|
-
replayFidelity: "low" as const,
|
|
84
|
-
tools: true,
|
|
85
|
-
usage: "partial" as const,
|
|
86
|
-
billing: "none" as const,
|
|
87
|
-
providerMetadata: false,
|
|
88
|
-
},
|
|
89
|
-
mock: {
|
|
90
|
-
nativeStreaming: false,
|
|
91
|
-
messageStreaming: true,
|
|
92
|
-
reasoningStreaming: false,
|
|
93
|
-
toolCallStreaming: true,
|
|
94
|
-
hiddenReasoningReplay: "none" as const,
|
|
95
|
-
replayFidelity: "high" as const,
|
|
96
|
-
tools: true,
|
|
97
|
-
usage: "none" as const,
|
|
98
|
-
billing: "none" as const,
|
|
99
|
-
providerMetadata: true,
|
|
100
|
-
},
|
|
101
|
-
} as const satisfies Record<string, AdapterCapabilities>;
|
|
102
|
-
|
|
103
22
|
// ── Adapter 接口 ──────────────────────────────────────────────
|
|
104
23
|
|
|
105
24
|
export interface BackendAdapter {
|
|
106
25
|
readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
107
|
-
readonly
|
|
26
|
+
readonly nativeStreaming: boolean;
|
|
108
27
|
stream(request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
|
|
109
28
|
}
|
|
110
29
|
|
package/src/types/index.ts
CHANGED
|
@@ -46,14 +46,4 @@ export type {
|
|
|
46
46
|
} from "./events.js";
|
|
47
47
|
|
|
48
48
|
// Adapter 协议和 client 类型
|
|
49
|
-
export type {
|
|
50
|
-
BackendAdapter,
|
|
51
|
-
AdapterCapabilities,
|
|
52
|
-
FetchFn,
|
|
53
|
-
NormalizedRequest,
|
|
54
|
-
CreateAIClientOptions,
|
|
55
|
-
AIClient,
|
|
56
|
-
} from "./adapter.js";
|
|
57
|
-
|
|
58
|
-
// 能力矩阵常量
|
|
59
|
-
export { CAPABILITY_MATRIX } from "./adapter.js";
|
|
49
|
+
export type { BackendAdapter, FetchFn, NormalizedRequest, CreateAIClientOptions, AIClient } from "./adapter.js";
|