@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/package.json
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "../helpers/mapping.js";
|
|
23
23
|
import { emitMalformedStreamWarning } from "../helpers/adapter-auxiliary.js";
|
|
24
24
|
|
|
25
|
-
import type {
|
|
25
|
+
import type { NormalizedRequest, AIStreamEvent, EventFactory, OutputItem, FetchFn } from "../index.js";
|
|
26
26
|
|
|
27
27
|
// ── 类型 ──────────────────────────────────────────────────────
|
|
28
28
|
|
|
@@ -249,29 +249,12 @@ function buildAssistantReplayMessage(params: {
|
|
|
249
249
|
|
|
250
250
|
export class ChatCompletionsAdapter extends AdapterBase {
|
|
251
251
|
readonly kind = "chat-completions" as const;
|
|
252
|
-
readonly
|
|
253
|
-
nativeStreaming: true,
|
|
254
|
-
messageStreaming: true,
|
|
255
|
-
reasoningStreaming: false,
|
|
256
|
-
toolCallStreaming: false,
|
|
257
|
-
hiddenReasoningReplay: "none" as const,
|
|
258
|
-
replayFidelity: "low" as const,
|
|
259
|
-
tools: true,
|
|
260
|
-
usage: "full" as const,
|
|
261
|
-
billing: "derived" as const,
|
|
262
|
-
providerMetadata: false,
|
|
263
|
-
};
|
|
252
|
+
readonly nativeStreaming = true;
|
|
264
253
|
|
|
265
254
|
private apiKey: string;
|
|
266
255
|
private baseUrl: string;
|
|
267
256
|
private fetchFn: FetchFn;
|
|
268
257
|
|
|
269
|
-
private markReasoningCompatibility(): void {
|
|
270
|
-
this.capabilities.reasoningStreaming = true;
|
|
271
|
-
this.capabilities.hiddenReasoningReplay = "partial";
|
|
272
|
-
this.capabilities.replayFidelity = "medium";
|
|
273
|
-
}
|
|
274
|
-
|
|
275
258
|
constructor(options: ChatCompletionsAdapterOptions) {
|
|
276
259
|
super();
|
|
277
260
|
this.apiKey = options.apiKey;
|
|
@@ -300,7 +283,7 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
300
283
|
item.role === "developer"
|
|
301
284
|
? "system"
|
|
302
285
|
: item.role === "system"
|
|
303
|
-
|
|
286
|
+
? "system"
|
|
304
287
|
: item.role === "user"
|
|
305
288
|
? "user"
|
|
306
289
|
: "assistant";
|
|
@@ -310,9 +293,10 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
310
293
|
}
|
|
311
294
|
case "tool_call": {
|
|
312
295
|
// 只允许附着到尾部 assistant turn,否则新建一个
|
|
313
|
-
const lastAssistant =
|
|
314
|
-
|
|
315
|
-
|
|
296
|
+
const lastAssistant =
|
|
297
|
+
messages.length > 0 && messages[messages.length - 1]?.role === "assistant"
|
|
298
|
+
? messages[messages.length - 1]
|
|
299
|
+
: null;
|
|
316
300
|
const tc: ChatToolCall = {
|
|
317
301
|
id: item.id,
|
|
318
302
|
type: "function",
|
|
@@ -439,7 +423,6 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
439
423
|
let currentReasoningId = "";
|
|
440
424
|
let hasMessageStarted = false;
|
|
441
425
|
let hasReasoningStarted = false;
|
|
442
|
-
let hasStreamedReasoning = false;
|
|
443
426
|
|
|
444
427
|
// tool_calls 累积: tool call index → { id, name, args }
|
|
445
428
|
const pendingToolCalls = new Map<number, PendingToolCall>();
|
|
@@ -540,7 +523,6 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
540
523
|
if (!hasReasoningStarted) {
|
|
541
524
|
currentReasoningId = `reason-${chunk.id}`;
|
|
542
525
|
hasReasoningStarted = true;
|
|
543
|
-
hasStreamedReasoning = true;
|
|
544
526
|
accumulatedReasoning = "";
|
|
545
527
|
yield factory.reasoningStarted(currentReasoningId, "full");
|
|
546
528
|
}
|
|
@@ -609,8 +591,6 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
609
591
|
yield event;
|
|
610
592
|
}
|
|
611
593
|
|
|
612
|
-
if (hasStreamedReasoning) this.markReasoningCompatibility();
|
|
613
|
-
|
|
614
594
|
// 构建 stop reason
|
|
615
595
|
const stopReason = mapStopReason(finishReason);
|
|
616
596
|
|
|
@@ -665,8 +645,6 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
665
645
|
if (hasMessageStarted || hasReasoningStarted || pendingToolCalls.size > 0) {
|
|
666
646
|
yield factory.responseWarning("Stream ended without a finish_reason", "INCOMPLETE_STREAM");
|
|
667
647
|
|
|
668
|
-
if (hasStreamedReasoning) this.markReasoningCompatibility();
|
|
669
|
-
|
|
670
648
|
const { events, assistantReplayMessage } = finalizePendingTurn();
|
|
671
649
|
for (const event of events) {
|
|
672
650
|
yield event;
|
package/src/adapters/index.ts
CHANGED
|
@@ -22,10 +22,13 @@ export type { OllamaAdapterOptions } from "./ollama.js";
|
|
|
22
22
|
export { MockAdapter } from "./mock.js";
|
|
23
23
|
export type {
|
|
24
24
|
MockAdapterOptions,
|
|
25
|
+
MockHistoryRecord,
|
|
26
|
+
MockTextStreamOptions,
|
|
25
27
|
MockInputExpectation,
|
|
26
28
|
MockRequestExpectation,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
MockHandlerContext,
|
|
30
|
+
MockHandler,
|
|
31
|
+
MockStaticHandler,
|
|
29
32
|
MockWarningStep,
|
|
30
33
|
MockAuxiliaryStep,
|
|
31
34
|
MockMessageStep,
|
|
@@ -37,5 +40,5 @@ export type {
|
|
|
37
40
|
MockInterruptStep,
|
|
38
41
|
MockThrowStep,
|
|
39
42
|
MockStep,
|
|
40
|
-
MockTurn,
|
|
41
43
|
} from "./mock.js";
|
|
44
|
+
export { assertMockRequest, withMockStreaming } from "./mock.js";
|
package/src/adapters/messages.ts
CHANGED
|
@@ -27,7 +27,6 @@ import { emitMalformedStreamWarning } from "../helpers/adapter-auxiliary.js";
|
|
|
27
27
|
|
|
28
28
|
import { parseSSEEvents } from "../helpers/sse-parser.js";
|
|
29
29
|
|
|
30
|
-
import { CAPABILITY_MATRIX } from "../index.js";
|
|
31
30
|
import type { NormalizedRequest, AIStreamEvent, EventFactory, OutputItem, FetchFn } from "../index.js";
|
|
32
31
|
|
|
33
32
|
// ── 类型 ──────────────────────────────────────────────────────
|
|
@@ -237,7 +236,7 @@ function buildStreamMetadata(options: {
|
|
|
237
236
|
|
|
238
237
|
export class MessagesAdapter extends AdapterBase {
|
|
239
238
|
readonly kind = "messages" as const;
|
|
240
|
-
readonly
|
|
239
|
+
readonly nativeStreaming = true;
|
|
241
240
|
|
|
242
241
|
private apiKey: string;
|
|
243
242
|
private apiVersion: string;
|
|
@@ -276,7 +275,9 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
276
275
|
if (item.role === "system" || item.role === "developer") {
|
|
277
276
|
// Anthropic 不支持 system/developer role 在 messages 中
|
|
278
277
|
// 合并到 system prompt
|
|
279
|
-
const text = contentBlocksToText(
|
|
278
|
+
const text = contentBlocksToText(
|
|
279
|
+
ensureMessagesTextBlocks(item.content, `input message (${item.role}) content`),
|
|
280
|
+
);
|
|
280
281
|
systemPrompt = systemPrompt ? `${systemPrompt}\n${text}` : text;
|
|
281
282
|
break;
|
|
282
283
|
}
|
|
@@ -297,9 +298,7 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
297
298
|
type: "tool_use",
|
|
298
299
|
id: item.id,
|
|
299
300
|
name: item.name,
|
|
300
|
-
input:
|
|
301
|
-
(item.argumentsJson as Record<string, unknown> | undefined) ??
|
|
302
|
-
parseToolUseInput(item.argumentsText),
|
|
301
|
+
input: (item.argumentsJson as Record<string, unknown> | undefined) ?? parseToolUseInput(item.argumentsText),
|
|
303
302
|
};
|
|
304
303
|
|
|
305
304
|
if (lastMsg && lastMsg.role === "assistant" && typeof lastMsg.content !== "string") {
|
|
@@ -346,7 +345,11 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
346
345
|
typeof b === "object" &&
|
|
347
346
|
b !== null &&
|
|
348
347
|
"type" in b &&
|
|
349
|
-
(b.type === "text" ||
|
|
348
|
+
(b.type === "text" ||
|
|
349
|
+
b.type === "thinking" ||
|
|
350
|
+
b.type === "redacted_thinking" ||
|
|
351
|
+
b.type === "tool_use" ||
|
|
352
|
+
b.type === "tool_result"),
|
|
350
353
|
);
|
|
351
354
|
if (isValidContent) {
|
|
352
355
|
rollbackTrailingAssistantMessages(messages);
|
|
@@ -405,7 +408,10 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
405
408
|
const auxiliary = this.createAuxiliaryState(request);
|
|
406
409
|
|
|
407
410
|
if (request.metadata) {
|
|
408
|
-
yield factory.responseWarning(
|
|
411
|
+
yield factory.responseWarning(
|
|
412
|
+
"Request metadata is not supported by the Messages adapter",
|
|
413
|
+
"UNSUPPORTED_METADATA",
|
|
414
|
+
);
|
|
409
415
|
}
|
|
410
416
|
|
|
411
417
|
const response = await this.fetchFn(`${this.baseUrl}/messages`, {
|
|
@@ -454,7 +460,10 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
454
460
|
|
|
455
461
|
if (request.include?.providerMetadata !== "off") {
|
|
456
462
|
const headerMetadata = pickProviderHeaders(response.headers);
|
|
457
|
-
auxiliary.recordProviderMetadata(
|
|
463
|
+
auxiliary.recordProviderMetadata(
|
|
464
|
+
"header",
|
|
465
|
+
Object.keys(headerMetadata).length > 0 ? { headers: headerMetadata } : undefined,
|
|
466
|
+
);
|
|
458
467
|
}
|
|
459
468
|
|
|
460
469
|
try {
|
|
@@ -614,9 +623,9 @@ export class MessagesAdapter extends AdapterBase {
|
|
|
614
623
|
if (u) {
|
|
615
624
|
auxiliary.recordUsage(
|
|
616
625
|
{
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
626
|
+
inputTokens: u.input_tokens,
|
|
627
|
+
outputTokens: u.output_tokens,
|
|
628
|
+
totalTokens: u.input_tokens + u.output_tokens,
|
|
620
629
|
},
|
|
621
630
|
"stream",
|
|
622
631
|
u,
|