@codehz/ai 0.1.3 → 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/README.md +84 -79
- package/dist/index.d.mts +18 -26
- package/dist/index.mjs +113 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/adapters/chat-completions.ts +5 -4
- package/src/adapters/index.ts +5 -3
- package/src/adapters/messages.ts +20 -10
- package/src/adapters/mock.ts +190 -160
- package/src/adapters/ollama.ts +30 -18
- package/src/adapters/responses.ts +4 -6
- package/src/core/validation.ts +21 -16
- package/src/helpers/adapter-auxiliary.ts +3 -1
- package/src/helpers/adapter-base.ts +5 -5
- package/src/helpers/index.ts +1 -5
- package/src/types/index.ts +1 -7
package/src/adapters/ollama.ts
CHANGED
|
@@ -207,19 +207,22 @@ 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 ───────────────────────────────────────────────────
|
|
@@ -264,7 +267,10 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
264
267
|
: item.role === "user"
|
|
265
268
|
? "user"
|
|
266
269
|
: "assistant";
|
|
267
|
-
messages.push({
|
|
270
|
+
messages.push({
|
|
271
|
+
role,
|
|
272
|
+
content: contentBlocksToText(ensureOllamaTextBlocks(item.content, `input message (${item.role}) content`)),
|
|
273
|
+
});
|
|
268
274
|
break;
|
|
269
275
|
}
|
|
270
276
|
case "tool_call": {
|
|
@@ -301,7 +307,12 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
301
307
|
}
|
|
302
308
|
case "opaque": {
|
|
303
309
|
// Best-effort restore from opaque replay
|
|
304
|
-
if (
|
|
310
|
+
if (
|
|
311
|
+
item.source === "ollama" &&
|
|
312
|
+
item.purpose === "replay" &&
|
|
313
|
+
typeof item.payload === "object" &&
|
|
314
|
+
item.payload !== null
|
|
315
|
+
) {
|
|
305
316
|
const payload = item.payload as Record<string, unknown>;
|
|
306
317
|
if (payload.role === "assistant" && typeof payload.content === "string") {
|
|
307
318
|
rollbackTrailingAssistantMessages(messages);
|
|
@@ -477,9 +488,10 @@ export class OllamaAdapter extends AdapterBase {
|
|
|
477
488
|
{
|
|
478
489
|
inputTokens: chunk.prompt_eval_count,
|
|
479
490
|
outputTokens: chunk.eval_count,
|
|
480
|
-
totalTokens:
|
|
481
|
-
|
|
482
|
-
|
|
491
|
+
totalTokens:
|
|
492
|
+
chunk.prompt_eval_count !== undefined && chunk.eval_count !== undefined
|
|
493
|
+
? chunk.prompt_eval_count + chunk.eval_count
|
|
494
|
+
: undefined,
|
|
483
495
|
},
|
|
484
496
|
"final",
|
|
485
497
|
{
|
|
@@ -164,9 +164,7 @@ function parseSSE(chunk: string): { events: ResponsesSSEEvent[]; rest: string; m
|
|
|
164
164
|
|
|
165
165
|
function isReplayCanonicalInput(item: ResponsesInputItem): boolean {
|
|
166
166
|
return (
|
|
167
|
-
(item.type === "message" && item.role === "assistant") ||
|
|
168
|
-
item.type === "reasoning" ||
|
|
169
|
-
item.type === "function_call"
|
|
167
|
+
(item.type === "message" && item.role === "assistant") || item.type === "reasoning" || item.type === "function_call"
|
|
170
168
|
);
|
|
171
169
|
}
|
|
172
170
|
|
|
@@ -452,9 +450,9 @@ export class ResponsesAdapter extends AdapterBase {
|
|
|
452
450
|
if (completedResponse.usage) {
|
|
453
451
|
auxiliary.recordUsage(
|
|
454
452
|
{
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
453
|
+
inputTokens: completedResponse.usage.input_tokens,
|
|
454
|
+
outputTokens: completedResponse.usage.output_tokens,
|
|
455
|
+
totalTokens: completedResponse.usage.total_tokens,
|
|
458
456
|
},
|
|
459
457
|
"final",
|
|
460
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
|
|
|
@@ -99,7 +99,9 @@ export class AdapterAuxiliaryState {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
if (this.request.include?.usage !== "off" && !built.usage) {
|
|
102
|
-
events.push(
|
|
102
|
+
events.push(
|
|
103
|
+
factory.responseWarning("Usage information was not provided by the provider", WarningCode.USAGE_MISSING),
|
|
104
|
+
);
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
if (this.request.include?.billing !== "off") {
|
|
@@ -112,7 +112,10 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
112
112
|
protected buildResponse(request: NormalizedRequest, result: StreamResult, _factory: EventFactory): AIResponse {
|
|
113
113
|
const text = this.extractText(result.output);
|
|
114
114
|
const warnings = mergeWarnings(result.warnings, _factory.warnings);
|
|
115
|
-
const auxiliary = mergeAuxiliary(
|
|
115
|
+
const auxiliary = mergeAuxiliary(
|
|
116
|
+
result.auxiliary,
|
|
117
|
+
result.providerMetadata ? { providerMetadata: result.providerMetadata } : undefined,
|
|
118
|
+
);
|
|
116
119
|
|
|
117
120
|
return {
|
|
118
121
|
id: request.requestId,
|
|
@@ -146,10 +149,7 @@ export abstract class AdapterBase implements BackendAdapter {
|
|
|
146
149
|
}
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
function mergeAuxiliary(
|
|
150
|
-
base?: Partial<AuxiliaryInfo>,
|
|
151
|
-
patch?: Partial<AuxiliaryInfo>,
|
|
152
|
-
): AuxiliaryInfo | undefined {
|
|
152
|
+
function mergeAuxiliary(base?: Partial<AuxiliaryInfo>, patch?: Partial<AuxiliaryInfo>): AuxiliaryInfo | undefined {
|
|
153
153
|
if (!base && !patch) return undefined;
|
|
154
154
|
|
|
155
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/index.ts
CHANGED
|
@@ -46,10 +46,4 @@ export type {
|
|
|
46
46
|
} from "./events.js";
|
|
47
47
|
|
|
48
48
|
// Adapter 协议和 client 类型
|
|
49
|
-
export type {
|
|
50
|
-
BackendAdapter,
|
|
51
|
-
FetchFn,
|
|
52
|
-
NormalizedRequest,
|
|
53
|
-
CreateAIClientOptions,
|
|
54
|
-
AIClient,
|
|
55
|
-
} from "./adapter.js";
|
|
49
|
+
export type { BackendAdapter, FetchFn, NormalizedRequest, CreateAIClientOptions, AIClient } from "./adapter.js";
|