@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/.oxlintrc.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"import/no-duplicates": "error",
|
|
12
12
|
"import/named": "error",
|
|
13
13
|
"import/namespace": "error",
|
|
14
|
+
"no-await-in-loop": "off",
|
|
14
15
|
"typescript/no-non-null-assertion": "warn",
|
|
15
16
|
"unicorn/no-null": "off",
|
|
16
17
|
"unicorn/no-array-for-each": "off",
|
|
@@ -24,22 +25,6 @@
|
|
|
24
25
|
"typescript/no-non-null-assertion": "off",
|
|
25
26
|
"unicorn/no-array-reverse": "off"
|
|
26
27
|
}
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"files": ["examples/**/*.ts"],
|
|
30
|
-
"rules": {
|
|
31
|
-
"no-await-in-loop": "off"
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"files": [
|
|
36
|
-
"src/adapters/chat-completions.ts",
|
|
37
|
-
"src/adapters/messages.ts",
|
|
38
|
-
"src/adapters/responses.ts"
|
|
39
|
-
],
|
|
40
|
-
"rules": {
|
|
41
|
-
"no-await-in-loop": "off"
|
|
42
|
-
}
|
|
43
28
|
}
|
|
44
29
|
],
|
|
45
30
|
"env": {
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
|
+
|
|
5
|
+
`src/` contains the library source. Keep canonical request/response logic in `src/core/`, provider integrations in `src/adapters/`, reusable stream utilities in `src/helpers/`, and shared types in `src/types/`. The public entrypoint is `src/index.ts`.
|
|
6
|
+
|
|
7
|
+
`tests/` holds Bun test suites plus shared fixtures such as `tests/fixtures.ts`. `examples/` contains runnable usage samples like `examples/basic.ts` and `examples/tool-loop.ts`. `dist/` is generated output from the packaging build and should not be edited by hand.
|
|
8
|
+
|
|
9
|
+
## Build, Test, and Development Commands
|
|
10
|
+
|
|
11
|
+
- `bun install` installs dependencies.
|
|
12
|
+
- `bun run typecheck` runs strict TypeScript validation without emitting files.
|
|
13
|
+
- `bun run lint` checks the codebase with `oxlint`; use `bun run lint:fix` for safe autofixes.
|
|
14
|
+
- `bun run format` applies `oxfmt`; `bun run format:check` verifies formatting in CI style.
|
|
15
|
+
- `bun test` runs the full Bun test suite.
|
|
16
|
+
- `bun run example:basic`, `bun run example:multi-turn`, and `bun run example:tool-loop` execute sample integrations.
|
|
17
|
+
- `bun run prepack` builds the package with `tsdown` into `dist/`.
|
|
18
|
+
|
|
19
|
+
## Coding Style & Naming Conventions
|
|
20
|
+
|
|
21
|
+
This repository uses TypeScript ESM with 2-space indentation, semicolons, double quotes, trailing commas, and LF line endings. `oxfmt` enforces formatting, and `oxlint` enforces import correctness and general safety rules.
|
|
22
|
+
|
|
23
|
+
Follow existing naming patterns: kebab-case filenames such as `chat-completions.ts`, PascalCase for exported classes and types, and camelCase for functions, variables, and helpers. Add public exports through the existing index files instead of reaching into deep paths from consumers.
|
|
24
|
+
|
|
25
|
+
## Testing Guidelines
|
|
26
|
+
|
|
27
|
+
Tests use `bun:test` and live in `tests/*.test.ts`. Name suites after the unit or scenario under test, for example `responses-adapter.test.ts` or `scenarios.test.ts`. Favor behavior-focused `describe`/`it` blocks and cover event ordering, replay round-trips, warnings, and adapter-specific edge cases. Use `MockAdapter` and shared fixtures when validating streaming behavior.
|
|
28
|
+
|
|
29
|
+
## Commit & Pull Request Guidelines
|
|
30
|
+
|
|
31
|
+
Recent history follows conventional prefixes such as `feat(mock): ...`, `refactor(types): ...`, `docs: ...`, `build: ...`, and `chore: ...`. Keep scopes aligned with the subsystem you changed.
|
|
32
|
+
|
|
33
|
+
Pull requests should summarize behavior changes, list verification commands run locally, and link the relevant issue when applicable. Include example output or event traces when changing stream semantics or adapter behavior.
|
|
34
|
+
|
|
35
|
+
## Configuration & Secrets
|
|
36
|
+
|
|
37
|
+
Use environment variables for provider credentials, such as `OPENAI_API_KEY`. Do not hardcode secrets in source, examples, or tests.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nano-ai
|
|
2
2
|
|
|
3
|
-
统一流式 AI
|
|
3
|
+
统一流式 AI 客户端,提供一套 canonical API,对接真实模型后端与面向测试的回调驱动 `MockAdapter`(`responses` / `messages` / `chat.completions` / `ollama` / `mock`)。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -109,98 +109,132 @@ console.log(response.replay); // 续接材料
|
|
|
109
109
|
|
|
110
110
|
## 后端 Adapter
|
|
111
111
|
|
|
112
|
-
| Adapter | 类 |
|
|
113
|
-
| ----------------------- | ------------------------ |
|
|
114
|
-
| OpenAI Responses API | `ResponsesAdapter` |
|
|
115
|
-
| Anthropic Messages API | `MessagesAdapter` |
|
|
116
|
-
| OpenAI Chat Completions | `ChatCompletionsAdapter` |
|
|
117
|
-
| Ollama Chat API | `OllamaAdapter` |
|
|
118
|
-
| Scripted Test Backend | `MockAdapter` |
|
|
112
|
+
| Adapter | 类 | 说明 |
|
|
113
|
+
| ----------------------- | ------------------------ | ----------------------- |
|
|
114
|
+
| OpenAI Responses API | `ResponsesAdapter` | OpenAI Responses 端点 |
|
|
115
|
+
| Anthropic Messages API | `MessagesAdapter` | Anthropic Messages 端点 |
|
|
116
|
+
| OpenAI Chat Completions | `ChatCompletionsAdapter` | Chat Completions 端点 |
|
|
117
|
+
| Ollama Chat API | `OllamaAdapter` | 本地或自托管 Ollama |
|
|
118
|
+
| Scripted Test Backend | `MockAdapter` | 脚本化测试夹具 |
|
|
119
119
|
|
|
120
120
|
```ts
|
|
121
|
-
import {
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
import {
|
|
122
|
+
ResponsesAdapter,
|
|
123
|
+
MessagesAdapter,
|
|
124
|
+
ChatCompletionsAdapter,
|
|
125
|
+
OllamaAdapter,
|
|
126
|
+
MockAdapter,
|
|
127
|
+
withMockStreaming,
|
|
128
|
+
} from "nano-ai";
|
|
129
|
+
|
|
130
|
+
// OpenAI Responses API
|
|
124
131
|
const responses = new ResponsesAdapter({ apiKey: "sk-..." });
|
|
125
132
|
|
|
126
133
|
// Anthropic Messages API
|
|
127
134
|
const messages = new MessagesAdapter({ apiKey: "sk-ant-..." });
|
|
128
135
|
|
|
129
|
-
// OpenAI Chat Completions
|
|
136
|
+
// OpenAI Chat Completions
|
|
130
137
|
const chat = new ChatCompletionsAdapter({ apiKey: "sk-..." });
|
|
131
138
|
|
|
132
139
|
// Ollama
|
|
133
140
|
const ollama = new OllamaAdapter({ baseUrl: "http://localhost:11434" });
|
|
134
141
|
|
|
135
|
-
//
|
|
142
|
+
// 面向测试的回调驱动 mock backend
|
|
136
143
|
const mock = new MockAdapter({
|
|
137
|
-
|
|
144
|
+
handler: withMockStreaming(
|
|
145
|
+
async function* () {
|
|
146
|
+
yield { type: "message", content: "我先调用天气工具。" };
|
|
147
|
+
yield {
|
|
148
|
+
type: "tool_call",
|
|
149
|
+
id: "mock-call-weather",
|
|
150
|
+
name: "get_weather",
|
|
151
|
+
argumentsText: '{"city":"Hangzhou"}',
|
|
152
|
+
};
|
|
153
|
+
},
|
|
138
154
|
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
{
|
|
142
|
-
type: "tool_call",
|
|
143
|
-
id: "mock-call-weather",
|
|
144
|
-
name: "get_weather",
|
|
145
|
-
argumentsText: '{"city":"Hangzhou"}',
|
|
146
|
-
},
|
|
147
|
-
],
|
|
155
|
+
charsPerSecond: 24,
|
|
156
|
+
chunkSize: 1,
|
|
148
157
|
},
|
|
149
|
-
|
|
158
|
+
),
|
|
150
159
|
});
|
|
151
160
|
```
|
|
152
161
|
|
|
153
|
-
|
|
162
|
+
公开 adapter 接口只暴露稳定的标识与流式来源:
|
|
154
163
|
|
|
155
164
|
```ts
|
|
156
|
-
adapter.
|
|
157
|
-
adapter.
|
|
158
|
-
adapter.capabilities.replayFidelity; // "high" | "medium" | "low"
|
|
165
|
+
adapter.kind; // "responses" | "messages" | "chat-completions" | ...
|
|
166
|
+
adapter.nativeStreaming; // 是否为 provider 原生流,而不是本地模拟分片
|
|
159
167
|
```
|
|
160
168
|
|
|
169
|
+
像 reasoning、tool call、`replay` 材料这类响应特征,应直接从本次事件流、warning 和 `replay` 内容判断。
|
|
170
|
+
|
|
161
171
|
## Mock 后端
|
|
162
172
|
|
|
163
|
-
`MockAdapter`
|
|
173
|
+
`MockAdapter` 是一个面向测试的回调驱动 adapter,用来验证长流程工具调用、`replay` 续接和异常路径。
|
|
174
|
+
|
|
175
|
+
如果你要调试前端逐字渲染效果,可以用 `withMockStreaming()` 给非流式 handler 包一层分片输出:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
const handler = withMockStreaming(
|
|
179
|
+
async function* () {
|
|
180
|
+
yield { type: "message", content: "Streaming preview for the frontend." };
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
charsPerSecond: 20, // 每秒约 20 个字符
|
|
184
|
+
chunkSize: 1, // 默认 1,即逐字输出
|
|
185
|
+
initialDelayMs: 150, // 可选:首字前停顿
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
默认会发出单个完整 `message.delta`。只有经 `withMockStreaming()` 注入默认流速后,`message` / `reasoning` / `tool_call` 参数才会被拆成多个 delta。单个 step 也可用 `stream: false` 关闭包装器的默认流速配置。
|
|
164
191
|
|
|
165
|
-
|
|
192
|
+
核心思路是每轮请求执行一次 handler:
|
|
166
193
|
|
|
167
|
-
-
|
|
168
|
-
-
|
|
194
|
+
- handler 会拿到 `request` 和 `context`
|
|
195
|
+
- `context` 内建 `previousReplay`、`pendingToolCalls`、`history`
|
|
196
|
+
- handler 可脚本化发出 `message` / `reasoning` / `tool_call`
|
|
169
197
|
- 可注入 `warning`、`content_filter`、transport interruption、provider-style error
|
|
170
|
-
-
|
|
198
|
+
- 可用 `assertMockRequest()` 验证调用方是否把上一轮 `replay` 和当前 `tool_result` 正确带回
|
|
171
199
|
|
|
172
200
|
```ts
|
|
173
|
-
import { createAIClient, MockAdapter } from "nano-ai";
|
|
201
|
+
import { assertMockRequest, createAIClient, MockAdapter } from "nano-ai";
|
|
174
202
|
|
|
175
203
|
const client = createAIClient({
|
|
176
204
|
adapter: new MockAdapter({
|
|
177
|
-
|
|
178
|
-
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
items: [{ type: "message", role: "user", textIncludes: "weather" }],
|
|
182
|
-
tools: "present",
|
|
183
|
-
toolChoice: "present",
|
|
184
|
-
},
|
|
185
|
-
steps: [
|
|
186
|
-
{ type: "message", content: "Checking weather now." },
|
|
205
|
+
handler: async function* (request, context) {
|
|
206
|
+
if (context.turnIndex === 0) {
|
|
207
|
+
assertMockRequest(
|
|
208
|
+
request,
|
|
187
209
|
{
|
|
188
|
-
type: "
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
argumentsText: '{"city":"Hangzhou"}',
|
|
210
|
+
items: [{ type: "message", role: "user", textIncludes: "weather" }],
|
|
211
|
+
tools: "present",
|
|
212
|
+
toolChoice: "present",
|
|
192
213
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
context,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
yield { type: "message", content: "Checking weather now." };
|
|
218
|
+
yield {
|
|
219
|
+
type: "tool_call",
|
|
220
|
+
id: "mock-call-weather",
|
|
221
|
+
name: "get_weather",
|
|
222
|
+
argumentsText: '{"city":"Hangzhou"}',
|
|
223
|
+
};
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
assertMockRequest(
|
|
228
|
+
request,
|
|
229
|
+
{
|
|
198
230
|
requireReplayFromPreviousTurn: true,
|
|
199
231
|
requireToolResultsForPendingCalls: true,
|
|
200
232
|
},
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
233
|
+
context,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
yield { type: "message", content: "Hangzhou is 28C and sunny." };
|
|
237
|
+
},
|
|
204
238
|
}),
|
|
205
239
|
model: "mock-model",
|
|
206
240
|
});
|
|
@@ -209,10 +243,13 @@ const client = createAIClient({
|
|
|
209
243
|
核心类型:
|
|
210
244
|
|
|
211
245
|
```ts
|
|
212
|
-
type
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
246
|
+
type MockHandler = (request: NormalizedRequest, context: MockHandlerContext) => AsyncIterable<MockStep>;
|
|
247
|
+
|
|
248
|
+
type MockHandlerContext = {
|
|
249
|
+
turnIndex: number;
|
|
250
|
+
previousReplay: ReplayItem[];
|
|
251
|
+
pendingToolCalls: readonly ToolCallItem[];
|
|
252
|
+
history: readonly MockHistoryRecord[];
|
|
216
253
|
};
|
|
217
254
|
```
|
|
218
255
|
|
|
@@ -224,21 +261,13 @@ type MockTurn = {
|
|
|
224
261
|
畸形路径示例:
|
|
225
262
|
|
|
226
263
|
```ts
|
|
227
|
-
{
|
|
228
|
-
|
|
229
|
-
{ type: "message", content: "partial answer" },
|
|
230
|
-
{ type: "interrupt" }, // 不发 response.completed,collectStream() 应失败
|
|
231
|
-
],
|
|
232
|
-
}
|
|
264
|
+
yield { type: "message", content: "partial answer" };
|
|
265
|
+
yield { type: "interrupt" }; // 不发 response.completed,collectStream() 应失败
|
|
233
266
|
```
|
|
234
267
|
|
|
235
268
|
```ts
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
{ type: "warning", message: "content filtered by policy", code: "CONTENT_FILTERED" },
|
|
239
|
-
{ type: "complete", stopReason: "content_filter" },
|
|
240
|
-
],
|
|
241
|
-
}
|
|
269
|
+
yield { type: "warning", message: "content filtered by policy", code: "CONTENT_FILTERED" };
|
|
270
|
+
yield { type: "complete", stopReason: "content_filter" };
|
|
242
271
|
```
|
|
243
272
|
|
|
244
273
|
## 多轮对话
|
|
@@ -321,7 +350,7 @@ const { usage, billing, auxiliary, warnings } = collector.build();
|
|
|
321
350
|
|
|
322
351
|
```bash
|
|
323
352
|
bun run typecheck # TypeScript 类型检查
|
|
324
|
-
bun run test #
|
|
353
|
+
bun run test # 运行全部测试
|
|
325
354
|
bun run example:basic
|
|
326
355
|
bun run example:multi-turn
|
|
327
356
|
bun run example:tool-loop
|
package/dist/index.d.mts
CHANGED
|
@@ -228,83 +228,9 @@ type NormalizedRequest = AIRequest & {
|
|
|
228
228
|
model: string;
|
|
229
229
|
requestId: string;
|
|
230
230
|
};
|
|
231
|
-
type AdapterCapabilities = {
|
|
232
|
-
nativeStreaming: boolean;
|
|
233
|
-
messageStreaming: boolean;
|
|
234
|
-
reasoningStreaming: boolean;
|
|
235
|
-
toolCallStreaming: boolean;
|
|
236
|
-
hiddenReasoningReplay: "full" | "partial" | "none";
|
|
237
|
-
replayFidelity: "high" | "medium" | "low";
|
|
238
|
-
tools: boolean;
|
|
239
|
-
usage: "full" | "partial" | "none";
|
|
240
|
-
billing: "direct" | "lookup" | "derived" | "none";
|
|
241
|
-
providerMetadata: boolean;
|
|
242
|
-
};
|
|
243
|
-
declare const CAPABILITY_MATRIX: {
|
|
244
|
-
readonly responses: {
|
|
245
|
-
readonly nativeStreaming: true;
|
|
246
|
-
readonly messageStreaming: true;
|
|
247
|
-
readonly reasoningStreaming: true;
|
|
248
|
-
readonly toolCallStreaming: true;
|
|
249
|
-
readonly hiddenReasoningReplay: "full";
|
|
250
|
-
readonly replayFidelity: "high";
|
|
251
|
-
readonly tools: true;
|
|
252
|
-
readonly usage: "full";
|
|
253
|
-
readonly billing: "lookup";
|
|
254
|
-
readonly providerMetadata: true;
|
|
255
|
-
};
|
|
256
|
-
readonly messages: {
|
|
257
|
-
readonly nativeStreaming: true;
|
|
258
|
-
readonly messageStreaming: true;
|
|
259
|
-
readonly reasoningStreaming: false;
|
|
260
|
-
readonly toolCallStreaming: true;
|
|
261
|
-
readonly hiddenReasoningReplay: "partial";
|
|
262
|
-
readonly replayFidelity: "medium";
|
|
263
|
-
readonly tools: true;
|
|
264
|
-
readonly usage: "full";
|
|
265
|
-
readonly billing: "lookup";
|
|
266
|
-
readonly providerMetadata: true;
|
|
267
|
-
};
|
|
268
|
-
readonly "chat.completions": {
|
|
269
|
-
readonly nativeStreaming: true;
|
|
270
|
-
readonly messageStreaming: true;
|
|
271
|
-
readonly reasoningStreaming: false;
|
|
272
|
-
readonly toolCallStreaming: false;
|
|
273
|
-
readonly hiddenReasoningReplay: "none";
|
|
274
|
-
readonly replayFidelity: "low";
|
|
275
|
-
readonly tools: true;
|
|
276
|
-
readonly usage: "full";
|
|
277
|
-
readonly billing: "derived";
|
|
278
|
-
readonly providerMetadata: false;
|
|
279
|
-
};
|
|
280
|
-
readonly ollama: {
|
|
281
|
-
readonly nativeStreaming: true;
|
|
282
|
-
readonly messageStreaming: true;
|
|
283
|
-
readonly reasoningStreaming: false;
|
|
284
|
-
readonly toolCallStreaming: false;
|
|
285
|
-
readonly hiddenReasoningReplay: "none";
|
|
286
|
-
readonly replayFidelity: "low";
|
|
287
|
-
readonly tools: true;
|
|
288
|
-
readonly usage: "partial";
|
|
289
|
-
readonly billing: "none";
|
|
290
|
-
readonly providerMetadata: false;
|
|
291
|
-
};
|
|
292
|
-
readonly mock: {
|
|
293
|
-
readonly nativeStreaming: false;
|
|
294
|
-
readonly messageStreaming: true;
|
|
295
|
-
readonly reasoningStreaming: false;
|
|
296
|
-
readonly toolCallStreaming: true;
|
|
297
|
-
readonly hiddenReasoningReplay: "none";
|
|
298
|
-
readonly replayFidelity: "high";
|
|
299
|
-
readonly tools: true;
|
|
300
|
-
readonly usage: "none";
|
|
301
|
-
readonly billing: "none";
|
|
302
|
-
readonly providerMetadata: true;
|
|
303
|
-
};
|
|
304
|
-
};
|
|
305
231
|
interface BackendAdapter {
|
|
306
232
|
readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
307
|
-
readonly
|
|
233
|
+
readonly nativeStreaming: boolean;
|
|
308
234
|
stream(request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
|
|
309
235
|
}
|
|
310
236
|
type CreateAIClientOptions = {
|
|
@@ -511,7 +437,6 @@ type BillingPostprocessHook = (context: {
|
|
|
511
437
|
usage?: Usage;
|
|
512
438
|
billing?: BillingInfo;
|
|
513
439
|
auxiliary?: AuxiliaryInfo;
|
|
514
|
-
capabilities: AdapterCapabilities;
|
|
515
440
|
}) => MaybePromise<Partial<BillingInfo> | undefined>;
|
|
516
441
|
type AuxiliaryFinalizeOptions = {
|
|
517
442
|
lookup?: () => Promise<LookupResult>;
|
|
@@ -529,10 +454,9 @@ type AuxiliaryFinalizeResult = {
|
|
|
529
454
|
};
|
|
530
455
|
declare class AdapterAuxiliaryState {
|
|
531
456
|
private readonly request;
|
|
532
|
-
private readonly capabilities;
|
|
533
457
|
private readonly collector;
|
|
534
458
|
private readonly metadataSources;
|
|
535
|
-
constructor(request: NormalizedRequest
|
|
459
|
+
constructor(request: NormalizedRequest);
|
|
536
460
|
recordUsage(usage: Partial<Usage>, source: UsageSource, raw?: unknown): void;
|
|
537
461
|
recordBilling(billing: Partial<BillingInfo>, source: BillingSource, raw?: unknown): void;
|
|
538
462
|
recordProviderMetadata(source: string, metadata: Record<string, unknown> | undefined): void;
|
|
@@ -566,7 +490,7 @@ type StreamResult = {
|
|
|
566
490
|
};
|
|
567
491
|
declare abstract class AdapterBase implements BackendAdapter {
|
|
568
492
|
abstract readonly kind: "chat-completions" | "messages" | "responses" | "ollama" | "mock";
|
|
569
|
-
abstract readonly
|
|
493
|
+
abstract readonly nativeStreaming: boolean;
|
|
570
494
|
/**
|
|
571
495
|
* stream 模板方法:
|
|
572
496
|
* 1. 创建事件工厂,发射 response.started
|
|
@@ -659,18 +583,7 @@ type ResponsesTool = {
|
|
|
659
583
|
};
|
|
660
584
|
declare class ResponsesAdapter extends AdapterBase {
|
|
661
585
|
readonly kind: "responses";
|
|
662
|
-
readonly
|
|
663
|
-
readonly nativeStreaming: true;
|
|
664
|
-
readonly messageStreaming: true;
|
|
665
|
-
readonly reasoningStreaming: true;
|
|
666
|
-
readonly toolCallStreaming: true;
|
|
667
|
-
readonly hiddenReasoningReplay: "full";
|
|
668
|
-
readonly replayFidelity: "high";
|
|
669
|
-
readonly tools: true;
|
|
670
|
-
readonly usage: "full";
|
|
671
|
-
readonly billing: "lookup";
|
|
672
|
-
readonly providerMetadata: true;
|
|
673
|
-
};
|
|
586
|
+
readonly nativeStreaming = true;
|
|
674
587
|
private apiKey;
|
|
675
588
|
private baseUrl;
|
|
676
589
|
private fetchFn;
|
|
@@ -738,18 +651,7 @@ type MessagesAPITool = {
|
|
|
738
651
|
};
|
|
739
652
|
declare class MessagesAdapter extends AdapterBase {
|
|
740
653
|
readonly kind: "messages";
|
|
741
|
-
readonly
|
|
742
|
-
readonly nativeStreaming: true;
|
|
743
|
-
readonly messageStreaming: true;
|
|
744
|
-
readonly reasoningStreaming: false;
|
|
745
|
-
readonly toolCallStreaming: true;
|
|
746
|
-
readonly hiddenReasoningReplay: "partial";
|
|
747
|
-
readonly replayFidelity: "medium";
|
|
748
|
-
readonly tools: true;
|
|
749
|
-
readonly usage: "full";
|
|
750
|
-
readonly billing: "lookup";
|
|
751
|
-
readonly providerMetadata: true;
|
|
752
|
-
};
|
|
654
|
+
readonly nativeStreaming = true;
|
|
753
655
|
private apiKey;
|
|
754
656
|
private apiVersion;
|
|
755
657
|
private baseUrl;
|
|
@@ -808,11 +710,10 @@ type ChatTool = {
|
|
|
808
710
|
};
|
|
809
711
|
declare class ChatCompletionsAdapter extends AdapterBase {
|
|
810
712
|
readonly kind: "chat-completions";
|
|
811
|
-
readonly
|
|
713
|
+
readonly nativeStreaming = true;
|
|
812
714
|
private apiKey;
|
|
813
715
|
private baseUrl;
|
|
814
716
|
private fetchFn;
|
|
815
|
-
private markReasoningCompatibility;
|
|
816
717
|
constructor(options: ChatCompletionsAdapterOptions);
|
|
817
718
|
protected buildRequest(request: NormalizedRequest): ChatRequest;
|
|
818
719
|
protected runStream(providerRequest: ChatRequest, factory: EventFactory, request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
|
|
@@ -857,7 +758,7 @@ type OllamaTool = {
|
|
|
857
758
|
};
|
|
858
759
|
declare class OllamaAdapter extends AdapterBase {
|
|
859
760
|
readonly kind: "ollama";
|
|
860
|
-
readonly
|
|
761
|
+
readonly nativeStreaming = true;
|
|
861
762
|
private baseUrl;
|
|
862
763
|
private apiKey;
|
|
863
764
|
private fetchFn;
|
|
@@ -896,13 +797,18 @@ type MockRequestExpectation = {
|
|
|
896
797
|
toolChoice?: "ignore" | "present" | "absent";
|
|
897
798
|
items?: MockInputExpectation[];
|
|
898
799
|
};
|
|
899
|
-
type
|
|
800
|
+
type MockHistoryRecord = {
|
|
801
|
+
turnIndex: number;
|
|
802
|
+
requestId: string;
|
|
803
|
+
replay: ReplayItem[];
|
|
804
|
+
toolCalls: ToolCallItem[];
|
|
805
|
+
};
|
|
806
|
+
type MockHandlerContext = {
|
|
900
807
|
turnIndex: number;
|
|
901
808
|
previousReplay: ReplayItem[];
|
|
902
809
|
pendingToolCalls: readonly ToolCallItem[];
|
|
903
|
-
history: readonly
|
|
810
|
+
history: readonly MockHistoryRecord[];
|
|
904
811
|
};
|
|
905
|
-
type MockTurnValidator = (request: NormalizedRequest, context: MockTurnContext) => void | Promise<void>;
|
|
906
812
|
type MockWarningStep = {
|
|
907
813
|
type: "warning";
|
|
908
814
|
message: string;
|
|
@@ -914,10 +820,25 @@ type MockAuxiliaryStep = {
|
|
|
914
820
|
billing?: BillingInfo;
|
|
915
821
|
auxiliary?: Partial<AuxiliaryInfo>;
|
|
916
822
|
};
|
|
823
|
+
type MockTextStreamOptions = {
|
|
824
|
+
/**
|
|
825
|
+
* 每秒吐出的字符数。未设置时仍会按 chunk 拆分,但不会额外等待。
|
|
826
|
+
*/
|
|
827
|
+
charsPerSecond?: number;
|
|
828
|
+
/**
|
|
829
|
+
* 每个 delta 最多包含多少个字符,默认 1。
|
|
830
|
+
*/
|
|
831
|
+
chunkSize?: number;
|
|
832
|
+
/**
|
|
833
|
+
* 首个 delta 发出前的延迟。
|
|
834
|
+
*/
|
|
835
|
+
initialDelayMs?: number;
|
|
836
|
+
};
|
|
917
837
|
type MockMessageStep = {
|
|
918
838
|
type: "message";
|
|
919
839
|
id?: string;
|
|
920
840
|
content: string | ContentBlock[];
|
|
841
|
+
stream?: MockTextStreamOptions | false;
|
|
921
842
|
};
|
|
922
843
|
type MockReasoningStep = {
|
|
923
844
|
type: "reasoning";
|
|
@@ -926,6 +847,7 @@ type MockReasoningStep = {
|
|
|
926
847
|
type: "reasoning";
|
|
927
848
|
}>["visibility"];
|
|
928
849
|
content: string | ContentBlock[];
|
|
850
|
+
stream?: MockTextStreamOptions | false;
|
|
929
851
|
};
|
|
930
852
|
type MockToolCallStep = {
|
|
931
853
|
type: "tool_call";
|
|
@@ -934,12 +856,14 @@ type MockToolCallStep = {
|
|
|
934
856
|
argumentsText: string;
|
|
935
857
|
argumentsJson?: unknown;
|
|
936
858
|
streamArguments?: boolean;
|
|
859
|
+
stream?: MockTextStreamOptions | false;
|
|
937
860
|
};
|
|
938
861
|
type MockOutputStep = {
|
|
939
862
|
type: "output";
|
|
940
863
|
item: Extract<OutputItem, {
|
|
941
864
|
type: "message" | "reasoning" | "tool_call";
|
|
942
865
|
}>;
|
|
866
|
+
stream?: MockTextStreamOptions | false;
|
|
943
867
|
};
|
|
944
868
|
type MockCompleteStep = {
|
|
945
869
|
type: "complete";
|
|
@@ -967,46 +891,24 @@ type MockThrowStep = {
|
|
|
967
891
|
error: string | Error;
|
|
968
892
|
};
|
|
969
893
|
type MockStep = MockWarningStep | MockAuxiliaryStep | MockMessageStep | MockReasoningStep | MockToolCallStep | MockOutputStep | MockCompleteStep | MockErrorStep | MockInterruptStep | MockThrowStep;
|
|
970
|
-
type
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
steps: MockStep[];
|
|
974
|
-
};
|
|
894
|
+
type MockHandler = (request: NormalizedRequest, context: MockHandlerContext) => AsyncIterable<MockStep>;
|
|
895
|
+
type MockHandlerSource = Iterable<MockStep> | AsyncIterable<MockStep>;
|
|
896
|
+
type MockStaticHandler = (request: NormalizedRequest, context: MockHandlerContext) => MockHandlerSource | Promise<MockHandlerSource>;
|
|
975
897
|
type MockAdapterOptions = {
|
|
976
|
-
|
|
977
|
-
onExhausted?: "throw" | "repeat-last" | "complete-empty";
|
|
898
|
+
handler: MockHandler;
|
|
978
899
|
providerMetadata?: Record<string, unknown>;
|
|
979
900
|
};
|
|
980
|
-
type MockTurnRecord = {
|
|
981
|
-
turnIndex: number;
|
|
982
|
-
turnName?: string;
|
|
983
|
-
requestId: string;
|
|
984
|
-
replay: ReplayItem[];
|
|
985
|
-
toolCalls: ToolCallItem[];
|
|
986
|
-
};
|
|
987
901
|
type MockProviderRequest = {
|
|
988
902
|
request: NormalizedRequest;
|
|
989
|
-
|
|
903
|
+
handlerResult: AsyncIterable<MockStep>;
|
|
990
904
|
turnIndex: number;
|
|
991
|
-
turnName?: string;
|
|
992
905
|
remainingPendingToolCalls: ToolCallItem[];
|
|
993
906
|
};
|
|
907
|
+
declare function assertMockRequest(request: NormalizedRequest, expectation: MockRequestExpectation, context: MockHandlerContext): void;
|
|
994
908
|
declare class MockAdapter extends AdapterBase {
|
|
995
909
|
readonly kind: "mock";
|
|
996
|
-
readonly
|
|
997
|
-
|
|
998
|
-
readonly messageStreaming: true;
|
|
999
|
-
readonly reasoningStreaming: false;
|
|
1000
|
-
readonly toolCallStreaming: true;
|
|
1001
|
-
readonly hiddenReasoningReplay: "none";
|
|
1002
|
-
readonly replayFidelity: "high";
|
|
1003
|
-
readonly tools: true;
|
|
1004
|
-
readonly usage: "none";
|
|
1005
|
-
readonly billing: "none";
|
|
1006
|
-
readonly providerMetadata: true;
|
|
1007
|
-
};
|
|
1008
|
-
private readonly turns;
|
|
1009
|
-
private readonly onExhausted;
|
|
910
|
+
readonly nativeStreaming = false;
|
|
911
|
+
private readonly handler;
|
|
1010
912
|
private readonly providerMetadata?;
|
|
1011
913
|
private cursor;
|
|
1012
914
|
private previousReplay;
|
|
@@ -1017,9 +919,9 @@ declare class MockAdapter extends AdapterBase {
|
|
|
1017
919
|
protected buildRequest(request: NormalizedRequest): Promise<MockProviderRequest>;
|
|
1018
920
|
protected runStream(providerRequest: unknown, factory: EventFactory, request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
|
|
1019
921
|
private finalizeTurn;
|
|
1020
|
-
private
|
|
1021
|
-
private buildTurnContext;
|
|
922
|
+
private buildHandlerContext;
|
|
1022
923
|
}
|
|
924
|
+
declare function withMockStreaming(handler: MockStaticHandler, options: MockTextStreamOptions): MockHandler;
|
|
1023
925
|
//#endregion
|
|
1024
926
|
//#region src/helpers/mapping.d.ts
|
|
1025
927
|
declare function mapStopReason(providerReason: string): StopReason;
|
|
@@ -1141,5 +1043,5 @@ type SyntheticStreamOptions = {
|
|
|
1141
1043
|
*/
|
|
1142
1044
|
declare function syntheticStream(options: SyntheticStreamOptions): AsyncIterable<AIStreamEvent>;
|
|
1143
1045
|
//#endregion
|
|
1144
|
-
export { type AIClient, AIError, AIMappingError, AIProviderError, type AIRequest, AIRequestError, type AIResponse, AIStreamError, type AIStreamEvent, AdapterAuxiliaryState, AdapterBase,
|
|
1046
|
+
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, type InputItem, type LookupResult, 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, OllamaAdapter, type OllamaAdapterOptions, type OpaqueItem, type OutputItem, 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 StreamResult, type SyntheticStreamOptions, type ToolCallCompletedEvent, type ToolCallDeltaEvent, type ToolCallItem, type ToolCallStartedEvent, type ToolChoice, type ToolDefinition, type ToolResultItem, type Usage, type UsageSource, type ValidationIssue, WarningCode, aggregateEvents, assertMockRequest, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createEventFactory, emitMalformedStreamWarning, extractText, imageBlock, instructionsToText, jsonBlock, mapReasoningVisibility, mapStopReason, messageItem, metadataSourceList, normalizeRequest, opaqueBlock, opaqueItem, parseSSEEvents, reasoningItem, replayFromOutput, syntheticStream, textBlock, toolCallItem, toolResultItem, validateRequest, withMockStreaming };
|
|
1145
1047
|
//# sourceMappingURL=index.d.mts.map
|