@codehz/ai 0.2.4 → 0.3.0
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 +3 -8
- package/dist/index.d.mts +12 -37
- package/dist/index.mjs +58 -130
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/chat-completions.ts +3 -22
- package/src/adapters/messages.ts +9 -28
- package/src/adapters/mock.ts +1 -10
- package/src/adapters/ollama.ts +25 -53
- package/src/adapters/responses.ts +16 -35
- package/src/core/validation.ts +19 -0
- package/src/helpers/adapter-base.ts +3 -3
- package/src/helpers/index.ts +0 -1
- package/src/helpers/mapping.ts +1 -2
- package/src/helpers/request-mapper.ts +18 -25
- package/src/types/adapter.ts +1 -12
- package/src/types/index.ts +1 -9
- package/src/types/items.ts +0 -1
package/README.md
CHANGED
|
@@ -164,19 +164,14 @@ const mock = new MockAdapter({
|
|
|
164
164
|
});
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
公开 adapter
|
|
167
|
+
公开 adapter 接口暴露稳定标识和流来源:
|
|
168
168
|
|
|
169
169
|
```ts
|
|
170
170
|
adapter.kind; // "responses" | "messages" | "chat-completions" | ...
|
|
171
|
-
adapter.
|
|
172
|
-
adapter.capabilities.reasoningStreaming;
|
|
173
|
-
adapter.capabilities.toolCallStreaming;
|
|
174
|
-
adapter.capabilities.replay; // "canonical" | "opaque" | "none"
|
|
175
|
-
adapter.capabilities.usage; // "stream" | "final" | "none"
|
|
176
|
-
adapter.capabilities.toolResultOutcomes;
|
|
171
|
+
adapter.isSyntheticStream;
|
|
177
172
|
```
|
|
178
173
|
|
|
179
|
-
响应级 `backend.isSyntheticStream`
|
|
174
|
+
响应级 `backend.isSyntheticStream` 使用同一标记;具体响应内容仍应从
|
|
180
175
|
本次事件流、warning 和 `replay` 判断。
|
|
181
176
|
|
|
182
177
|
## Mock 后端
|
package/dist/index.d.mts
CHANGED
|
@@ -42,7 +42,6 @@ type ToolCallItem = {
|
|
|
42
42
|
id: string;
|
|
43
43
|
name: string;
|
|
44
44
|
argumentsText: string;
|
|
45
|
-
argumentsJson?: unknown;
|
|
46
45
|
};
|
|
47
46
|
type ToolResultItem = {
|
|
48
47
|
type: "tool_result";
|
|
@@ -234,18 +233,9 @@ type NormalizedRequest = AIRequest & {
|
|
|
234
233
|
model: string;
|
|
235
234
|
requestId: string;
|
|
236
235
|
};
|
|
237
|
-
type StreamingCapability = "native" | "synthetic" | "none";
|
|
238
|
-
type AdapterCapabilities = {
|
|
239
|
-
readonly textStreaming: StreamingCapability;
|
|
240
|
-
readonly reasoningStreaming: StreamingCapability;
|
|
241
|
-
readonly toolCallStreaming: StreamingCapability;
|
|
242
|
-
readonly replay: "canonical" | "opaque" | "none";
|
|
243
|
-
readonly usage: "stream" | "final" | "none";
|
|
244
|
-
readonly toolResultOutcomes: ReadonlyArray<"success" | "error" | "rejected">;
|
|
245
|
-
};
|
|
246
236
|
interface BackendAdapter {
|
|
247
237
|
readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
248
|
-
readonly
|
|
238
|
+
readonly isSyntheticStream: boolean;
|
|
249
239
|
stream(request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
|
|
250
240
|
}
|
|
251
241
|
type CreateAIClientOptions = {
|
|
@@ -525,7 +515,7 @@ type StreamResult = {
|
|
|
525
515
|
};
|
|
526
516
|
declare abstract class AdapterBase implements BackendAdapter {
|
|
527
517
|
abstract readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
528
|
-
abstract readonly
|
|
518
|
+
abstract readonly isSyntheticStream: boolean;
|
|
529
519
|
/**
|
|
530
520
|
* stream 模板方法:
|
|
531
521
|
* 1. 创建事件工厂,发射 response.started
|
|
@@ -618,7 +608,7 @@ type ResponsesTool = {
|
|
|
618
608
|
};
|
|
619
609
|
declare class ResponsesAdapter extends AdapterBase {
|
|
620
610
|
readonly kind: "responses";
|
|
621
|
-
readonly
|
|
611
|
+
readonly isSyntheticStream = false;
|
|
622
612
|
private apiKey;
|
|
623
613
|
private baseUrl;
|
|
624
614
|
private fetchFn;
|
|
@@ -686,7 +676,7 @@ type MessagesAPITool = {
|
|
|
686
676
|
};
|
|
687
677
|
declare class MessagesAdapter extends AdapterBase {
|
|
688
678
|
readonly kind: "messages";
|
|
689
|
-
readonly
|
|
679
|
+
readonly isSyntheticStream = false;
|
|
690
680
|
private apiKey;
|
|
691
681
|
private apiVersion;
|
|
692
682
|
private baseUrl;
|
|
@@ -744,7 +734,7 @@ type ChatTool = {
|
|
|
744
734
|
};
|
|
745
735
|
declare class ChatCompletionsAdapter extends AdapterBase {
|
|
746
736
|
readonly kind: "chat-completions";
|
|
747
|
-
readonly
|
|
737
|
+
readonly isSyntheticStream = false;
|
|
748
738
|
private apiKey;
|
|
749
739
|
private baseUrl;
|
|
750
740
|
private fetchFn;
|
|
@@ -792,7 +782,7 @@ type OllamaTool = {
|
|
|
792
782
|
};
|
|
793
783
|
declare class OllamaAdapter extends AdapterBase {
|
|
794
784
|
readonly kind: "ollama";
|
|
795
|
-
readonly
|
|
785
|
+
readonly isSyntheticStream = false;
|
|
796
786
|
private baseUrl;
|
|
797
787
|
private apiKey;
|
|
798
788
|
private fetchFn;
|
|
@@ -889,7 +879,6 @@ type MockToolCallStep = {
|
|
|
889
879
|
id: string;
|
|
890
880
|
name: string;
|
|
891
881
|
argumentsText: string;
|
|
892
|
-
argumentsJson?: unknown;
|
|
893
882
|
streamArguments?: boolean;
|
|
894
883
|
stream?: MockTextStreamOptions | false;
|
|
895
884
|
};
|
|
@@ -942,14 +931,7 @@ type MockProviderRequest = {
|
|
|
942
931
|
declare function assertMockRequest(request: NormalizedRequest, expectation: MockRequestExpectation, context: MockHandlerContext): void;
|
|
943
932
|
declare class MockAdapter extends AdapterBase {
|
|
944
933
|
readonly kind: "mock";
|
|
945
|
-
readonly
|
|
946
|
-
readonly textStreaming: "synthetic";
|
|
947
|
-
readonly reasoningStreaming: "synthetic";
|
|
948
|
-
readonly toolCallStreaming: "synthetic";
|
|
949
|
-
readonly replay: "canonical";
|
|
950
|
-
readonly usage: "final";
|
|
951
|
-
readonly toolResultOutcomes: readonly ["success", "error", "rejected"];
|
|
952
|
-
};
|
|
934
|
+
readonly isSyntheticStream = true;
|
|
953
935
|
private readonly handler;
|
|
954
936
|
private readonly providerMetadata?;
|
|
955
937
|
private cursor;
|
|
@@ -982,7 +964,7 @@ declare function opaqueBlock(payload: unknown): ContentBlock & {
|
|
|
982
964
|
};
|
|
983
965
|
declare function messageItem(content: ContentBlock[], overrides?: Partial<Omit<MessageItem, "type" | "content">>): MessageItem;
|
|
984
966
|
declare function reasoningItem(content: ContentBlock[], visibility?: ReasoningItem["visibility"], id?: string): ReasoningItem;
|
|
985
|
-
declare function toolCallItem(id: string, name: string, argumentsText: string
|
|
967
|
+
declare function toolCallItem(id: string, name: string, argumentsText: string): ToolCallItem;
|
|
986
968
|
declare function toolResultItem(callId: string, toolName: string, outcome: ToolResultItem["outcome"], content: ContentBlock[]): ToolResultItem;
|
|
987
969
|
declare function opaqueItem(source: OpaqueItem["source"], purpose: OpaqueItem["purpose"], payload: unknown, id?: string): OpaqueItem;
|
|
988
970
|
/**
|
|
@@ -1193,27 +1175,20 @@ declare function splitLines(buffer: string, allowEOF: boolean): StreamSplitResul
|
|
|
1193
1175
|
declare function splitSSEFrames(buffer: string, allowEOF: boolean): StreamSplitResult;
|
|
1194
1176
|
//#endregion
|
|
1195
1177
|
//#region src/helpers/request-mapper.d.ts
|
|
1196
|
-
type ProviderProfile = {
|
|
1197
|
-
readonly kind: string;
|
|
1198
|
-
readonly instructionsMode: "system_message" | "instructions_field" | "none";
|
|
1199
|
-
readonly supportedBlockTypes: ReadonlyArray<ContentBlock["type"]>;
|
|
1200
|
-
readonly reasoningBlockTypes: ReadonlyArray<ContentBlock["type"]>;
|
|
1201
|
-
readonly capabilities: AdapterCapabilities;
|
|
1202
|
-
};
|
|
1203
1178
|
declare class NormalizedRequestMapper {
|
|
1204
|
-
readonly
|
|
1205
|
-
constructor(
|
|
1179
|
+
readonly kind: string;
|
|
1180
|
+
constructor(kind: string);
|
|
1206
1181
|
mapInstructions(instructions: string | InstructionBlock[]): string;
|
|
1207
1182
|
ensureTextBlocks(blocks: ContentBlock[], field: string): ContentBlock[];
|
|
1208
1183
|
ensureReasoningBlocks(blocks: ContentBlock[], field: string): Array<Extract<ContentBlock, {
|
|
1209
1184
|
type: "text";
|
|
1210
1185
|
}>>;
|
|
1211
|
-
|
|
1186
|
+
parseToolArguments(item: ToolCallItem): Record<string, unknown>;
|
|
1212
1187
|
rollbackTrailingAssistantMessages<T extends {
|
|
1213
1188
|
role: string;
|
|
1214
1189
|
}>(messages: T[]): void;
|
|
1215
1190
|
private ensureBlocks;
|
|
1216
1191
|
}
|
|
1217
1192
|
//#endregion
|
|
1218
|
-
export { type AIClient, AIError, AIMappingError, AIProviderError, type AIRequest, AIRequestError, type AIResponse, AIStreamError, type AIStreamEvent, AdapterAuxiliaryState, AdapterBase,
|
|
1193
|
+
export { type AIClient, AIError, AIMappingError, AIProviderError, type AIRequest, AIRequestError, type AIResponse, AIStreamError, type AIStreamEvent, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, type AuxiliaryFinalizeOptions, type AuxiliaryFinalizeResult, type AuxiliaryInfo, type BackendAdapter, type BackendTrace, type BillingInfo, type BillingPostprocessHook, type BillingSource, ChatCompletionsAdapter, type ChatCompletionsAdapterOptions, type ContentBlock, type CreateAIClientOptions, type EventFactory, type EventFactoryBackend, type EventFactoryState, type FetchFn, type IncludeSettings, IncrementalStreamParser, type InputItem, type InstructionBlock, type JsonContentBlock, type LookupResult, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, type MessageCompletedEvent, type MessageDeltaEvent, type MessageItem, type MessageStartedEvent, MessagesAdapter, type MessagesAdapterOptions, MockAdapter, type MockAdapterOptions, type MockAuxiliaryStep, type MockCompleteStep, type MockErrorStep, type MockHandler, type MockHandlerContext, type MockHistoryRecord, type MockInputExpectation, type MockInterruptStep, type MockMessageStep, type MockOutputStep, type MockReasoningStep, type MockRequestExpectation, type MockStaticHandler, type MockStep, type MockTextStreamOptions, type MockThrowStep, type MockToolCallStep, type MockWarningStep, type NormalizeOptions, type NormalizedRequest, NormalizedRequestMapper, OllamaAdapter, type OllamaAdapterOptions, type OpaqueEnvelopeResult, type OpaqueItem, type OutputItem, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, type ReasoningCompletedEvent, type ReasoningDeltaEvent, type ReasoningItem, type ReasoningStartedEvent, type ReplayItem, type ResponseAuxiliaryEvent, type ResponseCompletedEvent, type ResponseStartedEvent, type ResponseWarningEvent, ResponsesAdapter, type ResponsesAdapterOptions, type SSEEvent, type StopReason, type StreamEventBase, type StreamParseResult, type StreamResult, type StreamSplitResult, type SyntheticStreamOptions, type TextContentBlock, type ToolCallCompletedEvent, type ToolCallDeltaEvent, type ToolCallItem, type ToolCallStartedEvent, type ToolChoice, type ToolDefinition, type ToolResultItem, type Usage, type UsageSource, type ValidationIssue, WarningCode, aggregateEvents, assertMockRequest, assertOpaqueReplayEnvelope, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createEventFactory, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock, instructionsToText, jsonBlock, mapReasoningVisibility, mapStopReason, measureJsonDepth, messageItem, metadataSourceList, normalizeRequest, opaqueBlock, opaqueItem, parseSSEEvents, providerHttpError, reasoningItem, replayFromOutput, splitLines, splitSSEFrames, syntheticStream, textBlock, toolCallItem, toolResultItem, usageFromAnthropicMessages, usageFromChatCompletions, usageFromOllama, usageFromOpenAIResponses, validateOpaqueReplayEnvelope, validateRequest, withMockStreaming };
|
|
1219
1194
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -156,6 +156,12 @@ function validateInputItem(item, field, issues) {
|
|
|
156
156
|
if (typeof item.id !== "string" || item.id.length === 0) pushIssue(issues, `${field}.id`, "TOOL_CALL_ID_INVALID", `${field}.id must be a non-empty string`);
|
|
157
157
|
if (typeof item.name !== "string" || item.name.length === 0) pushIssue(issues, `${field}.name`, "TOOL_CALL_NAME_INVALID", `${field}.name must be a non-empty string`);
|
|
158
158
|
if (typeof item.argumentsText !== "string") pushIssue(issues, `${field}.argumentsText`, "TOOL_CALL_ARGUMENTS_INVALID", `${field}.argumentsText must be a string`);
|
|
159
|
+
else try {
|
|
160
|
+
const parsed = JSON.parse(item.argumentsText);
|
|
161
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) pushIssue(issues, `${field}.argumentsText`, "TOOL_CALL_ARGUMENTS_INVALID", `${field}.argumentsText must encode a JSON object`);
|
|
162
|
+
} catch {
|
|
163
|
+
pushIssue(issues, `${field}.argumentsText`, "TOOL_CALL_ARGUMENTS_INVALID", `${field}.argumentsText must encode a JSON object`);
|
|
164
|
+
}
|
|
159
165
|
return;
|
|
160
166
|
case "tool_result":
|
|
161
167
|
if (typeof item.callId !== "string" || item.callId.length === 0) pushIssue(issues, `${field}.callId`, "TOOL_RESULT_CALL_ID_INVALID", `${field}.callId must be a non-empty string`);
|
|
@@ -820,13 +826,12 @@ function reasoningItem(content, visibility = "full", id) {
|
|
|
820
826
|
content
|
|
821
827
|
};
|
|
822
828
|
}
|
|
823
|
-
function toolCallItem(id, name, argumentsText
|
|
829
|
+
function toolCallItem(id, name, argumentsText) {
|
|
824
830
|
return {
|
|
825
831
|
type: "tool_call",
|
|
826
832
|
id,
|
|
827
833
|
name,
|
|
828
|
-
argumentsText
|
|
829
|
-
argumentsJson
|
|
834
|
+
argumentsText
|
|
830
835
|
};
|
|
831
836
|
}
|
|
832
837
|
function toolResultItem(callId, toolName, outcome, content) {
|
|
@@ -1095,7 +1100,7 @@ var AdapterBase = class {
|
|
|
1095
1100
|
responseId: request.requestId,
|
|
1096
1101
|
backend: {
|
|
1097
1102
|
kind: this.kind,
|
|
1098
|
-
isSynthetic: this.
|
|
1103
|
+
isSynthetic: this.isSyntheticStream
|
|
1099
1104
|
}
|
|
1100
1105
|
});
|
|
1101
1106
|
yield factory.responseStarted(request.model);
|
|
@@ -1147,7 +1152,7 @@ var AdapterBase = class {
|
|
|
1147
1152
|
requestId: request.requestId,
|
|
1148
1153
|
rawResponseId: result.rawResponseId,
|
|
1149
1154
|
adapter: this.kind,
|
|
1150
|
-
isSyntheticStream: this.
|
|
1155
|
+
isSyntheticStream: this.isSyntheticStream,
|
|
1151
1156
|
metadataSources: result.metadataSources,
|
|
1152
1157
|
warnings
|
|
1153
1158
|
}
|
|
@@ -1566,25 +1571,25 @@ function splitSSEFrames(buffer, allowEOF) {
|
|
|
1566
1571
|
//#endregion
|
|
1567
1572
|
//#region src/helpers/request-mapper.ts
|
|
1568
1573
|
var NormalizedRequestMapper = class {
|
|
1569
|
-
|
|
1570
|
-
constructor(
|
|
1571
|
-
this.
|
|
1574
|
+
kind;
|
|
1575
|
+
constructor(kind) {
|
|
1576
|
+
this.kind = kind;
|
|
1572
1577
|
}
|
|
1573
1578
|
mapInstructions(instructions) {
|
|
1574
1579
|
return typeof instructions === "string" ? instructions : contentBlocksToText(this.ensureTextBlocks(instructions, "instructions"));
|
|
1575
1580
|
}
|
|
1576
1581
|
ensureTextBlocks(blocks, field) {
|
|
1577
|
-
return this.ensureBlocks(blocks, field,
|
|
1582
|
+
return this.ensureBlocks(blocks, field, ["text", "json"], "only text/json blocks are supported");
|
|
1578
1583
|
}
|
|
1579
1584
|
ensureReasoningBlocks(blocks, field) {
|
|
1580
|
-
return this.ensureBlocks(blocks, field,
|
|
1585
|
+
return this.ensureBlocks(blocks, field, ["text"], "reasoning only supports text blocks");
|
|
1581
1586
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
throw new AIRequestError(`${this.
|
|
1587
|
+
parseToolArguments(item) {
|
|
1588
|
+
try {
|
|
1589
|
+
const parsed = JSON.parse(item.argumentsText);
|
|
1590
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
1591
|
+
} catch {}
|
|
1592
|
+
throw new AIRequestError(`${this.kind} requires tool_call argumentsText to be a valid JSON object`, "TOOL_CALL_ARGUMENTS_INVALID");
|
|
1588
1593
|
}
|
|
1589
1594
|
rollbackTrailingAssistantMessages(messages) {
|
|
1590
1595
|
while (messages.length > 0 && messages[messages.length - 1]?.role === "assistant") messages.pop();
|
|
@@ -1592,7 +1597,7 @@ var NormalizedRequestMapper = class {
|
|
|
1592
1597
|
ensureBlocks(blocks, field, supportedTypes, description) {
|
|
1593
1598
|
for (let i = 0; i < blocks.length; i++) {
|
|
1594
1599
|
const block = blocks[i];
|
|
1595
|
-
if (block && !supportedTypes.includes(block.type)) throw new AIRequestError(`${this.
|
|
1600
|
+
if (block && !supportedTypes.includes(block.type)) throw new AIRequestError(`${this.kind} does not support ${field}[${i}] of type "${block.type}"; ${description}`, "UNSUPPORTED_CONTENT_BLOCK");
|
|
1596
1601
|
}
|
|
1597
1602
|
return blocks;
|
|
1598
1603
|
}
|
|
@@ -1609,21 +1614,7 @@ var NormalizedRequestMapper = class {
|
|
|
1609
1614
|
*
|
|
1610
1615
|
* 支持消息流 / reasoning 流 / tool_call 流及高保真 replay。
|
|
1611
1616
|
*/
|
|
1612
|
-
const
|
|
1613
|
-
kind: "responses",
|
|
1614
|
-
instructionsMode: "instructions_field",
|
|
1615
|
-
supportedBlockTypes: ["text", "json"],
|
|
1616
|
-
reasoningBlockTypes: ["text"],
|
|
1617
|
-
capabilities: {
|
|
1618
|
-
textStreaming: "native",
|
|
1619
|
-
reasoningStreaming: "native",
|
|
1620
|
-
toolCallStreaming: "native",
|
|
1621
|
-
replay: "opaque",
|
|
1622
|
-
usage: "final",
|
|
1623
|
-
toolResultOutcomes: ["success"]
|
|
1624
|
-
}
|
|
1625
|
-
};
|
|
1626
|
-
const mapper$3 = new NormalizedRequestMapper(profile$3);
|
|
1617
|
+
const mapper$3 = new NormalizedRequestMapper("responses");
|
|
1627
1618
|
/** 已处理或可安全忽略的 Responses SSE 类型(未知类型会 warning 一次)。 */
|
|
1628
1619
|
const KNOWN_RESPONSES_SSE_TYPES = /* @__PURE__ */ new Set([
|
|
1629
1620
|
"response.output_item.added",
|
|
@@ -1632,8 +1623,6 @@ const KNOWN_RESPONSES_SSE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1632
1623
|
"response.output_text.done",
|
|
1633
1624
|
"response.reasoning.delta",
|
|
1634
1625
|
"response.reasoning.done",
|
|
1635
|
-
"response.tool_call.delta",
|
|
1636
|
-
"response.tool_call.done",
|
|
1637
1626
|
"response.function_call_arguments.delta",
|
|
1638
1627
|
"response.function_call_arguments.done",
|
|
1639
1628
|
"response.content_part.added",
|
|
@@ -1669,7 +1658,7 @@ function canonicalToResponsesBlock(b) {
|
|
|
1669
1658
|
}
|
|
1670
1659
|
var ResponsesAdapter = class extends AdapterBase {
|
|
1671
1660
|
kind = "responses";
|
|
1672
|
-
|
|
1661
|
+
isSyntheticStream = false;
|
|
1673
1662
|
apiKey;
|
|
1674
1663
|
baseUrl;
|
|
1675
1664
|
fetchFn;
|
|
@@ -1716,7 +1705,6 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1716
1705
|
});
|
|
1717
1706
|
break;
|
|
1718
1707
|
case "tool_result": {
|
|
1719
|
-
mapper$3.assertToolResultOutcome(item.outcome);
|
|
1720
1708
|
const output = mapper$3.ensureTextBlocks(item.content, `tool_result ${item.callId} content`).map(blockToText).join("\n");
|
|
1721
1709
|
input.push({
|
|
1722
1710
|
type: "function_call_output",
|
|
@@ -1814,6 +1802,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1814
1802
|
let completedEmitted = false;
|
|
1815
1803
|
let unknownEventsWarned = false;
|
|
1816
1804
|
const messageItemsWithDelta = /* @__PURE__ */ new Set();
|
|
1805
|
+
const toolCallNames = /* @__PURE__ */ new Map();
|
|
1817
1806
|
try {
|
|
1818
1807
|
while (true) {
|
|
1819
1808
|
const { done, value } = await reader.read().catch((err) => {
|
|
@@ -1841,9 +1830,12 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1841
1830
|
case "reasoning":
|
|
1842
1831
|
yield factory.reasoningStarted(item.id, "full");
|
|
1843
1832
|
break;
|
|
1844
|
-
case "function_call":
|
|
1845
|
-
|
|
1833
|
+
case "function_call": {
|
|
1834
|
+
const name = typeof item.name === "string" ? item.name : "unknown";
|
|
1835
|
+
toolCallNames.set(item.id, name);
|
|
1836
|
+
yield factory.toolCallStarted(item.id, name);
|
|
1846
1837
|
break;
|
|
1838
|
+
}
|
|
1847
1839
|
}
|
|
1848
1840
|
continue;
|
|
1849
1841
|
}
|
|
@@ -1871,14 +1863,14 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1871
1863
|
output.push(reasoningItem([textBlock(data.text)], "full", data.item_id));
|
|
1872
1864
|
continue;
|
|
1873
1865
|
}
|
|
1874
|
-
if (sseEvent.type === "response.
|
|
1866
|
+
if (sseEvent.type === "response.function_call_arguments.delta") {
|
|
1875
1867
|
const data = sseEvent.data;
|
|
1876
|
-
if (data.delta
|
|
1868
|
+
if (data.delta) yield factory.toolCallDelta(data.item_id, { argumentsText: data.delta });
|
|
1877
1869
|
continue;
|
|
1878
1870
|
}
|
|
1879
|
-
if (sseEvent.type === "response.
|
|
1871
|
+
if (sseEvent.type === "response.function_call_arguments.done") {
|
|
1880
1872
|
const data = sseEvent.data;
|
|
1881
|
-
const tcItem = toolCallItem(data.item_id, data.
|
|
1873
|
+
const tcItem = toolCallItem(data.item_id, toolCallNames.get(data.item_id) ?? "unknown", data.arguments);
|
|
1882
1874
|
yield factory.toolCallCompleted(data.item_id);
|
|
1883
1875
|
output.push(tcItem);
|
|
1884
1876
|
continue;
|
|
@@ -1975,21 +1967,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1975
1967
|
* - 高保真 replay(含 opaque continuation)
|
|
1976
1968
|
* - 能力降级 warning
|
|
1977
1969
|
*/
|
|
1978
|
-
const
|
|
1979
|
-
kind: "messages",
|
|
1980
|
-
instructionsMode: "system_message",
|
|
1981
|
-
supportedBlockTypes: ["text", "json"],
|
|
1982
|
-
reasoningBlockTypes: ["text"],
|
|
1983
|
-
capabilities: {
|
|
1984
|
-
textStreaming: "native",
|
|
1985
|
-
reasoningStreaming: "native",
|
|
1986
|
-
toolCallStreaming: "synthetic",
|
|
1987
|
-
replay: "opaque",
|
|
1988
|
-
usage: "stream",
|
|
1989
|
-
toolResultOutcomes: ["success", "error"]
|
|
1990
|
-
}
|
|
1991
|
-
};
|
|
1992
|
-
const mapper$2 = new NormalizedRequestMapper(profile$2);
|
|
1970
|
+
const mapper$2 = new NormalizedRequestMapper("messages");
|
|
1993
1971
|
function isMessagesReplayContentBlock(value) {
|
|
1994
1972
|
if (!value || typeof value !== "object" || !("type" in value)) return false;
|
|
1995
1973
|
const block = value;
|
|
@@ -2015,10 +1993,10 @@ function assertMessagesReplayContent(content) {
|
|
|
2015
1993
|
function synthesizeItemId(kind, blockIndex, responseId) {
|
|
2016
1994
|
return `${kind}-${blockIndex}-${responseId}`;
|
|
2017
1995
|
}
|
|
2018
|
-
function
|
|
1996
|
+
function parseProviderToolUseInput(input) {
|
|
2019
1997
|
try {
|
|
2020
1998
|
const parsed = JSON.parse(input);
|
|
2021
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1999
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
2022
2000
|
} catch {
|
|
2023
2001
|
return {};
|
|
2024
2002
|
}
|
|
@@ -2059,7 +2037,7 @@ function buildStreamMetadata(options) {
|
|
|
2059
2037
|
}
|
|
2060
2038
|
var MessagesAdapter = class extends AdapterBase {
|
|
2061
2039
|
kind = "messages";
|
|
2062
|
-
|
|
2040
|
+
isSyntheticStream = false;
|
|
2063
2041
|
apiKey;
|
|
2064
2042
|
apiVersion;
|
|
2065
2043
|
baseUrl;
|
|
@@ -2098,7 +2076,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2098
2076
|
type: "tool_use",
|
|
2099
2077
|
id: item.id,
|
|
2100
2078
|
name: item.name,
|
|
2101
|
-
input:
|
|
2079
|
+
input: mapper$2.parseToolArguments(item)
|
|
2102
2080
|
};
|
|
2103
2081
|
if (lastMsg && lastMsg.role === "assistant" && typeof lastMsg.content !== "string") lastMsg.content.push(toolBlock);
|
|
2104
2082
|
else messages.push({
|
|
@@ -2108,13 +2086,12 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2108
2086
|
break;
|
|
2109
2087
|
}
|
|
2110
2088
|
case "tool_result": {
|
|
2111
|
-
mapper$2.assertToolResultOutcome(item.outcome);
|
|
2112
2089
|
const content = mapper$2.ensureTextBlocks(item.content, `tool_result ${item.callId} content`).map(blockToText).join("\n");
|
|
2113
2090
|
const block = {
|
|
2114
2091
|
type: "tool_result",
|
|
2115
2092
|
tool_use_id: item.callId,
|
|
2116
2093
|
content,
|
|
2117
|
-
is_error: item.outcome
|
|
2094
|
+
is_error: item.outcome !== "success"
|
|
2118
2095
|
};
|
|
2119
2096
|
if (pendingToolResultMessage && typeof pendingToolResultMessage.content !== "string") pendingToolResultMessage.content.push(block);
|
|
2120
2097
|
else {
|
|
@@ -2140,7 +2117,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2140
2117
|
break;
|
|
2141
2118
|
}
|
|
2142
2119
|
case "opaque": {
|
|
2143
|
-
if (item.purpose !== "replay") break;
|
|
2120
|
+
if (item.source !== "messages" || item.purpose !== "replay") break;
|
|
2144
2121
|
assertOpaqueReplayEnvelope(item.payload);
|
|
2145
2122
|
const payload = item.payload;
|
|
2146
2123
|
if (payload.role === "assistant" && "content" in payload) {
|
|
@@ -2369,7 +2346,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2369
2346
|
type: "tool_use",
|
|
2370
2347
|
id: currentItemId,
|
|
2371
2348
|
name: currentToolName,
|
|
2372
|
-
input:
|
|
2349
|
+
input: parseProviderToolUseInput(currentArgsText || argsBuffer)
|
|
2373
2350
|
});
|
|
2374
2351
|
}
|
|
2375
2352
|
currentItemType = null;
|
|
@@ -2454,21 +2431,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2454
2431
|
* - replay fidelity 依赖 provider 是否暴露可回放的 assistant turn 字段
|
|
2455
2432
|
*/
|
|
2456
2433
|
const REASONING_FIELDS = ["reasoning_content", "reasoning"];
|
|
2457
|
-
const
|
|
2458
|
-
kind: "chat-completions",
|
|
2459
|
-
instructionsMode: "system_message",
|
|
2460
|
-
supportedBlockTypes: ["text", "json"],
|
|
2461
|
-
reasoningBlockTypes: ["text"],
|
|
2462
|
-
capabilities: {
|
|
2463
|
-
textStreaming: "native",
|
|
2464
|
-
reasoningStreaming: "native",
|
|
2465
|
-
toolCallStreaming: "native",
|
|
2466
|
-
replay: "opaque",
|
|
2467
|
-
usage: "final",
|
|
2468
|
-
toolResultOutcomes: ["success"]
|
|
2469
|
-
}
|
|
2470
|
-
};
|
|
2471
|
-
const mapper$1 = new NormalizedRequestMapper(profile$1);
|
|
2434
|
+
const mapper$1 = new NormalizedRequestMapper("chat-completions");
|
|
2472
2435
|
function extractReasoningText(value) {
|
|
2473
2436
|
if (typeof value === "string") return value;
|
|
2474
2437
|
if (Array.isArray(value)) return value.map(extractReasoningText).join("");
|
|
@@ -2545,7 +2508,7 @@ function buildAssistantReplayMessage(params) {
|
|
|
2545
2508
|
}
|
|
2546
2509
|
var ChatCompletionsAdapter = class extends AdapterBase {
|
|
2547
2510
|
kind = "chat-completions";
|
|
2548
|
-
|
|
2511
|
+
isSyntheticStream = false;
|
|
2549
2512
|
apiKey;
|
|
2550
2513
|
baseUrl;
|
|
2551
2514
|
fetchFn;
|
|
@@ -2590,7 +2553,6 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2590
2553
|
break;
|
|
2591
2554
|
}
|
|
2592
2555
|
case "tool_result":
|
|
2593
|
-
mapper$1.assertToolResultOutcome(item.outcome);
|
|
2594
2556
|
messages.push({
|
|
2595
2557
|
role: "tool",
|
|
2596
2558
|
tool_call_id: item.callId,
|
|
@@ -2605,7 +2567,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2605
2567
|
});
|
|
2606
2568
|
break;
|
|
2607
2569
|
case "opaque": {
|
|
2608
|
-
if (item.purpose !== "replay") break;
|
|
2570
|
+
if (item.source !== "chat.completions" || item.purpose !== "replay") break;
|
|
2609
2571
|
assertOpaqueReplayEnvelope(item.payload);
|
|
2610
2572
|
const payload = item.payload;
|
|
2611
2573
|
if (payload.role === "assistant" && typeof payload.content === "string") messages.push({
|
|
@@ -2920,29 +2882,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2920
2882
|
* - tool_call 不支持逐 token 流式
|
|
2921
2883
|
* - replay 保真度低(无 opaque continuation 机制)
|
|
2922
2884
|
*/
|
|
2923
|
-
const
|
|
2924
|
-
kind: "ollama",
|
|
2925
|
-
instructionsMode: "system_message",
|
|
2926
|
-
supportedBlockTypes: ["text", "json"],
|
|
2927
|
-
reasoningBlockTypes: ["text"],
|
|
2928
|
-
capabilities: {
|
|
2929
|
-
textStreaming: "native",
|
|
2930
|
-
reasoningStreaming: "none",
|
|
2931
|
-
toolCallStreaming: "synthetic",
|
|
2932
|
-
replay: "opaque",
|
|
2933
|
-
usage: "final",
|
|
2934
|
-
toolResultOutcomes: ["success"]
|
|
2935
|
-
}
|
|
2936
|
-
};
|
|
2937
|
-
const mapper = new NormalizedRequestMapper(profile);
|
|
2938
|
-
function parseOllamaToolArguments(item) {
|
|
2939
|
-
if (item.argumentsJson && typeof item.argumentsJson === "object" && item.argumentsJson !== null) return item.argumentsJson;
|
|
2940
|
-
try {
|
|
2941
|
-
const parsed = JSON.parse(item.argumentsText);
|
|
2942
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
2943
|
-
} catch {}
|
|
2944
|
-
throw new AIRequestError("ollama tool_call argumentsText must be valid JSON object when argumentsJson is absent", "TOOL_CALL_ARGUMENTS_INVALID");
|
|
2945
|
-
}
|
|
2885
|
+
const mapper = new NormalizedRequestMapper("ollama");
|
|
2946
2886
|
function isOllamaReplayToolCalls(value) {
|
|
2947
2887
|
return Array.isArray(value) && value.every((entry) => {
|
|
2948
2888
|
if (!entry || typeof entry !== "object" || !("function" in entry)) return false;
|
|
@@ -2960,7 +2900,7 @@ function toWireOllamaToolCalls(toolCalls) {
|
|
|
2960
2900
|
}
|
|
2961
2901
|
var OllamaAdapter = class extends AdapterBase {
|
|
2962
2902
|
kind = "ollama";
|
|
2963
|
-
|
|
2903
|
+
isSyntheticStream = false;
|
|
2964
2904
|
baseUrl;
|
|
2965
2905
|
apiKey;
|
|
2966
2906
|
fetchFn;
|
|
@@ -2971,7 +2911,6 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2971
2911
|
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
2972
2912
|
}
|
|
2973
2913
|
buildRequest(request) {
|
|
2974
|
-
if (request.toolChoice && request.toolChoice !== "auto") throw new AIRequestError("ollama does not support explicit toolChoice", "UNSUPPORTED_TOOL_CHOICE");
|
|
2975
2914
|
const messages = [];
|
|
2976
2915
|
/** Local-only name → call id queue for best-effort tool_result association (not sent to Ollama). */
|
|
2977
2916
|
const callIdsByName = /* @__PURE__ */ new Map();
|
|
@@ -2992,7 +2931,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2992
2931
|
const lastAssistant = messages.findLast((m) => m.role === "assistant");
|
|
2993
2932
|
const tc = { function: {
|
|
2994
2933
|
name: item.name,
|
|
2995
|
-
arguments:
|
|
2934
|
+
arguments: mapper.parseToolArguments(item)
|
|
2996
2935
|
} };
|
|
2997
2936
|
const queue = callIdsByName.get(item.name) ?? [];
|
|
2998
2937
|
queue.push(item.id);
|
|
@@ -3006,7 +2945,6 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3006
2945
|
break;
|
|
3007
2946
|
}
|
|
3008
2947
|
case "tool_result": {
|
|
3009
|
-
mapper.assertToolResultOutcome(item.outcome);
|
|
3010
2948
|
const queue = callIdsByName.get(item.toolName);
|
|
3011
2949
|
if (queue && queue.length > 0) queue.shift();
|
|
3012
2950
|
messages.push({
|
|
@@ -3053,7 +2991,9 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3053
2991
|
messages,
|
|
3054
2992
|
stream: true
|
|
3055
2993
|
};
|
|
3056
|
-
|
|
2994
|
+
const toolChoice = request.toolChoice;
|
|
2995
|
+
const selectedTools = toolChoice === "none" ? [] : toolChoice && typeof toolChoice === "object" ? request.tools?.filter((tool) => tool.name === toolChoice.name) : request.tools;
|
|
2996
|
+
if (selectedTools && selectedTools.length > 0) body.tools = selectedTools.map((t) => ({
|
|
3057
2997
|
type: "function",
|
|
3058
2998
|
function: {
|
|
3059
2999
|
name: t.name,
|
|
@@ -3071,6 +3011,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3071
3011
|
async *runStream(providerRequest, factory, request) {
|
|
3072
3012
|
const auxiliary = this.createAuxiliaryState(request);
|
|
3073
3013
|
let completedEmitted = false;
|
|
3014
|
+
if (request.toolChoice && request.toolChoice !== "auto") yield factory.responseWarning(request.toolChoice === "none" ? "Ollama toolChoice none was mapped by omitting tools" : `Ollama cannot force tool choice; only tool "${request.toolChoice.name}" was provided as a best-effort constraint`, WarningCode.CAPABILITY_DOWNGRADE);
|
|
3074
3015
|
if (request.metadata) yield factory.responseWarning("Request metadata is not supported by the Ollama adapter", "UNSUPPORTED_METADATA");
|
|
3075
3016
|
const headers = { "Content-Type": "application/json" };
|
|
3076
3017
|
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
@@ -3128,7 +3069,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3128
3069
|
id: tc.id,
|
|
3129
3070
|
function: {
|
|
3130
3071
|
name: tc.name,
|
|
3131
|
-
arguments: tc.
|
|
3072
|
+
arguments: JSON.parse(tc.argumentsText)
|
|
3132
3073
|
}
|
|
3133
3074
|
}))
|
|
3134
3075
|
}));
|
|
@@ -3189,8 +3130,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3189
3130
|
pendingToolCalls.push({
|
|
3190
3131
|
id: tcId,
|
|
3191
3132
|
name: tc.function.name,
|
|
3192
|
-
argumentsText: argsText
|
|
3193
|
-
argumentsJson: tc.function.arguments
|
|
3133
|
+
argumentsText: argsText
|
|
3194
3134
|
});
|
|
3195
3135
|
}
|
|
3196
3136
|
if (chunk.done) {
|
|
@@ -3206,7 +3146,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3206
3146
|
}
|
|
3207
3147
|
if (pendingToolCalls.length > 0) yield factory.responseWarning(`Ollama delivered ${pendingToolCalls.length} tool call(s) as a batch; tool_call streaming is not supported`, WarningCode.TOOL_CALL_BATCHED);
|
|
3208
3148
|
for (const pending of pendingToolCalls) {
|
|
3209
|
-
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText
|
|
3149
|
+
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText);
|
|
3210
3150
|
yield factory.toolCallStarted(pending.id, pending.name);
|
|
3211
3151
|
yield factory.toolCallDelta(pending.id, { argumentsText: pending.argumentsText });
|
|
3212
3152
|
yield factory.toolCallCompleted(pending.id);
|
|
@@ -3248,7 +3188,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3248
3188
|
}
|
|
3249
3189
|
if (pendingToolCalls.length > 0) yield factory.responseWarning(`Ollama delivered ${pendingToolCalls.length} tool call(s) as a batch; tool_call streaming is not supported`, WarningCode.TOOL_CALL_BATCHED);
|
|
3250
3190
|
for (const pending of pendingToolCalls) {
|
|
3251
|
-
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText
|
|
3191
|
+
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText);
|
|
3252
3192
|
yield factory.toolCallStarted(pending.id, pending.name);
|
|
3253
3193
|
yield factory.toolCallDelta(pending.id, { argumentsText: pending.argumentsText });
|
|
3254
3194
|
yield factory.toolCallCompleted(pending.id);
|
|
@@ -3289,18 +3229,7 @@ function assertMockRequest(request, expectation, context) {
|
|
|
3289
3229
|
}
|
|
3290
3230
|
var MockAdapter = class extends AdapterBase {
|
|
3291
3231
|
kind = "mock";
|
|
3292
|
-
|
|
3293
|
-
textStreaming: "synthetic",
|
|
3294
|
-
reasoningStreaming: "synthetic",
|
|
3295
|
-
toolCallStreaming: "synthetic",
|
|
3296
|
-
replay: "canonical",
|
|
3297
|
-
usage: "final",
|
|
3298
|
-
toolResultOutcomes: [
|
|
3299
|
-
"success",
|
|
3300
|
-
"error",
|
|
3301
|
-
"rejected"
|
|
3302
|
-
]
|
|
3303
|
-
};
|
|
3232
|
+
isSyntheticStream = true;
|
|
3304
3233
|
handler;
|
|
3305
3234
|
providerMetadata;
|
|
3306
3235
|
cursor = 0;
|
|
@@ -3508,8 +3437,7 @@ function createToolCallFromStep(step) {
|
|
|
3508
3437
|
type: "tool_call",
|
|
3509
3438
|
id: step.id,
|
|
3510
3439
|
name: step.name,
|
|
3511
|
-
argumentsText: step.argumentsText
|
|
3512
|
-
argumentsJson: step.argumentsJson
|
|
3440
|
+
argumentsText: step.argumentsText
|
|
3513
3441
|
};
|
|
3514
3442
|
}
|
|
3515
3443
|
function normalizeBlocks(content) {
|