@xsai/shared-chat 0.4.4 → 0.5.0-beta.2
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/dist/index.d.ts +52 -5
- package/dist/index.js +83 -21
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WithUnknown, CommonRequestOptions } from '@xsai/shared';
|
|
2
2
|
|
|
3
|
-
type FinishReason = 'content_filter' | 'error' | 'length' | 'other' | 'stop' | 'tool-calls' | (string & {});
|
|
3
|
+
type FinishReason = 'content_filter' | 'error' | 'length' | 'other' | 'stop' | 'tool-calls' | 'tool_calls' | (string & {});
|
|
4
4
|
|
|
5
5
|
interface AudioContentPart {
|
|
6
6
|
input_audio: {
|
|
@@ -150,6 +150,33 @@ interface ToolChoiceTool {
|
|
|
150
150
|
type: 'function';
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
type PrepareStep = (options: PrepareStepOptions) => PrepareStepResult | Promise<PrepareStepResult>;
|
|
154
|
+
interface PrepareStepOptions {
|
|
155
|
+
messages: Message[];
|
|
156
|
+
model: string;
|
|
157
|
+
stepNumber: number;
|
|
158
|
+
steps: CompletionStep[];
|
|
159
|
+
}
|
|
160
|
+
interface PrepareStepResult {
|
|
161
|
+
messages?: Message[];
|
|
162
|
+
model?: string;
|
|
163
|
+
toolChoice?: ToolChoice;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type StopCondition = (context: StopContext) => boolean;
|
|
167
|
+
interface StopContext {
|
|
168
|
+
messages: readonly Message[];
|
|
169
|
+
step: StopStep;
|
|
170
|
+
steps: readonly StopStep[];
|
|
171
|
+
}
|
|
172
|
+
interface StopStep {
|
|
173
|
+
finishReason: FinishReason;
|
|
174
|
+
text?: string;
|
|
175
|
+
toolCalls: CompletionToolCall[];
|
|
176
|
+
toolResults: CompletionToolResult[];
|
|
177
|
+
usage?: Usage;
|
|
178
|
+
}
|
|
179
|
+
|
|
153
180
|
/** @see {@link https://platform.openai.com/docs/api-reference/chat/create} */
|
|
154
181
|
interface ChatOptions extends CommonRequestOptions {
|
|
155
182
|
/**
|
|
@@ -188,12 +215,12 @@ declare const chat: <T extends WithUnknown<ChatOptions>>(options: T) => Promise<
|
|
|
188
215
|
|
|
189
216
|
interface DetermineStepTypeOptions {
|
|
190
217
|
finishReason: FinishReason;
|
|
191
|
-
maxSteps: number;
|
|
192
218
|
stepsLength: number;
|
|
193
219
|
toolCallsLength: number;
|
|
220
|
+
willContinue: boolean;
|
|
194
221
|
}
|
|
195
222
|
/** @internal */
|
|
196
|
-
declare const determineStepType: ({ finishReason,
|
|
223
|
+
declare const determineStepType: ({ finishReason, stepsLength, toolCallsLength, willContinue }: DetermineStepTypeOptions) => CompletionStepType;
|
|
197
224
|
|
|
198
225
|
interface ExecuteToolOptions {
|
|
199
226
|
abortSignal?: AbortSignal;
|
|
@@ -208,5 +235,25 @@ interface ExecuteToolResult {
|
|
|
208
235
|
}
|
|
209
236
|
declare const executeTool: ({ abortSignal, messages, toolCall, tools }: ExecuteToolOptions) => Promise<ExecuteToolResult>;
|
|
210
237
|
|
|
211
|
-
|
|
212
|
-
|
|
238
|
+
interface ResolvedStepOptions {
|
|
239
|
+
messages: Message[];
|
|
240
|
+
model: string;
|
|
241
|
+
toolChoice: ToolChoice | undefined;
|
|
242
|
+
}
|
|
243
|
+
interface ResolveStepOptionsOptions extends Pick<WithUnknown<ChatOptions>, 'messages' | 'model' | 'toolChoice'> {
|
|
244
|
+
prepareStep?: PrepareStep;
|
|
245
|
+
stepNumber: number;
|
|
246
|
+
steps: CompletionStep[];
|
|
247
|
+
}
|
|
248
|
+
declare const resolveStepOptions: ({ messages, model, prepareStep, stepNumber, steps, toolChoice }: ResolveStepOptionsOptions) => Promise<ResolvedStepOptions>;
|
|
249
|
+
|
|
250
|
+
declare const and: (...conditions: StopCondition[]) => StopCondition;
|
|
251
|
+
declare const or: (...conditions: StopCondition[]) => StopCondition;
|
|
252
|
+
declare const not: (condition: StopCondition) => StopCondition;
|
|
253
|
+
declare const stepCountAtLeast: (count: number) => StopCondition;
|
|
254
|
+
declare const hasToolCall: (name?: string) => StopCondition;
|
|
255
|
+
/** @internal */
|
|
256
|
+
declare const shouldStop: (stopWhen: StopCondition, context: StopContext) => boolean;
|
|
257
|
+
|
|
258
|
+
export { and, chat, determineStepType, executeTool, hasToolCall, not, or, resolveStepOptions, shouldStop, stepCountAtLeast };
|
|
259
|
+
export type { AssistantMessage, AudioContentPart, ChatOptions, CommonContentPart, CompletionStep, CompletionStepType, CompletionToolCall, CompletionToolResult, DetermineStepTypeOptions, DeveloperMessage, ExecuteToolOptions, ExecuteToolResult, FileContentPart, FinishReason, ImageContentPart, Message, PrepareStep, PrepareStepOptions, PrepareStepResult, RefusalContentPart, ResolveStepOptionsOptions, ResolvedStepOptions, StopCondition, StopContext, StopStep, SystemMessage, TextContentPart, Tool, ToolCall, ToolChoice, ToolExecuteOptions, ToolExecuteResult, ToolMessage, Usage, UserMessage };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { requestURL, requestHeaders, requestBody, responseCatch } from '@xsai/shared';
|
|
1
|
+
import { requestURL, requestHeaders, requestBody, responseCatch, InvalidToolCallError, InvalidToolInputError, ToolExecutionError } from '@xsai/shared';
|
|
2
2
|
|
|
3
3
|
const chat = async (options) => (options.fetch ?? globalThis.fetch)(requestURL("chat/completions", options.baseURL), {
|
|
4
4
|
body: requestBody({
|
|
5
5
|
...options,
|
|
6
|
-
tools: options.tools?.map(({ execute, ...tool }) => tool)
|
|
6
|
+
tools: options.tools?.map(({ execute: _execute, ...tool }) => tool)
|
|
7
7
|
}),
|
|
8
8
|
headers: requestHeaders({
|
|
9
9
|
"Content-Type": "application/json",
|
|
@@ -13,11 +13,11 @@ const chat = async (options) => (options.fetch ?? globalThis.fetch)(requestURL("
|
|
|
13
13
|
signal: options.abortSignal
|
|
14
14
|
}).then(responseCatch);
|
|
15
15
|
|
|
16
|
-
const determineStepType = ({ finishReason,
|
|
16
|
+
const determineStepType = ({ finishReason, stepsLength, toolCallsLength, willContinue }) => {
|
|
17
17
|
if (stepsLength === 0) {
|
|
18
18
|
return "initial";
|
|
19
|
-
} else if (
|
|
20
|
-
if (toolCallsLength > 0 &&
|
|
19
|
+
} else if (willContinue) {
|
|
20
|
+
if (toolCallsLength > 0 && ["tool-calls", "tool_calls"].includes(finishReason))
|
|
21
21
|
return "tool-result";
|
|
22
22
|
else if (!["error", "length"].includes(finishReason))
|
|
23
23
|
return "continue";
|
|
@@ -36,34 +36,75 @@ const wrapToolResult = (result) => {
|
|
|
36
36
|
return JSON.stringify(result);
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const isAbortError = (error, abortSignal) => abortSignal?.aborted === true && error === abortSignal.reason || error instanceof Error && error.name === "AbortError";
|
|
40
|
+
const parseToolInput = (toolName, input) => {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(input.trim() || "{}");
|
|
43
|
+
} catch (cause) {
|
|
44
|
+
throw new InvalidToolInputError(`Failed to parse tool input for "${toolName}".`, {
|
|
45
|
+
cause,
|
|
46
|
+
toolInput: input,
|
|
47
|
+
toolName
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const runTool = async (tool, options) => {
|
|
52
|
+
try {
|
|
53
|
+
return wrapToolResult(await tool.execute(options.parsedArgs, {
|
|
54
|
+
abortSignal: options.abortSignal,
|
|
55
|
+
messages: options.messages,
|
|
56
|
+
toolCallId: options.toolCall.id
|
|
57
|
+
}));
|
|
58
|
+
} catch (cause) {
|
|
59
|
+
if (isAbortError(cause, options.abortSignal))
|
|
60
|
+
throw cause;
|
|
61
|
+
throw new ToolExecutionError(`Tool "${tool.function.name}" execution failed.`, {
|
|
62
|
+
cause,
|
|
63
|
+
toolCallId: options.toolCall.id,
|
|
64
|
+
toolInput: options.parsedArgs,
|
|
65
|
+
toolName: tool.function.name
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
39
69
|
const executeTool = async ({ abortSignal, messages, toolCall, tools }) => {
|
|
40
|
-
const
|
|
70
|
+
const toolName = toolCall.function.name;
|
|
71
|
+
const toolArguments = toolCall.function.arguments;
|
|
72
|
+
if (toolName == null) {
|
|
73
|
+
throw new InvalidToolCallError(`Missing toolCall.function.name: ${JSON.stringify(toolCall)}`, {
|
|
74
|
+
reason: "missing_name",
|
|
75
|
+
toolCall
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (toolArguments == null) {
|
|
79
|
+
throw new InvalidToolCallError(`Missing toolCall.function.arguments: ${JSON.stringify(toolCall)}`, {
|
|
80
|
+
reason: "missing_arguments",
|
|
81
|
+
toolCall
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const tool = tools?.find((tool2) => tool2.function.name === toolName);
|
|
41
85
|
if (!tool) {
|
|
42
86
|
const availableTools = tools?.map((tool2) => tool2.function.name);
|
|
43
87
|
const availableToolsErrorMsg = availableTools == null || availableTools.length === 0 ? "No tools are available" : `Available tools: ${availableTools.join(", ")}`;
|
|
44
|
-
throw new
|
|
88
|
+
throw new InvalidToolCallError(`Model tried to call unavailable tool "${toolName}", ${availableToolsErrorMsg}.`, {
|
|
89
|
+
availableTools,
|
|
90
|
+
reason: "unknown_tool",
|
|
91
|
+
toolCall,
|
|
92
|
+
toolName
|
|
93
|
+
});
|
|
45
94
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (toolCall.function.arguments == null)
|
|
49
|
-
throw new Error(`Missing toolCall.function.arguments: ${JSON.stringify(toolCall)}`);
|
|
50
|
-
const parsedArgs = JSON.parse(toolCall.function.arguments.trim() || "{}");
|
|
51
|
-
const result = wrapToolResult(await tool.execute(parsedArgs, {
|
|
52
|
-
abortSignal,
|
|
53
|
-
messages,
|
|
54
|
-
toolCallId: toolCall.id
|
|
55
|
-
}));
|
|
95
|
+
const parsedArgs = parseToolInput(toolName, toolArguments);
|
|
96
|
+
const result = await runTool(tool, { abortSignal, messages, parsedArgs, toolCall });
|
|
56
97
|
const completionToolCall = {
|
|
57
|
-
args:
|
|
98
|
+
args: toolArguments,
|
|
58
99
|
toolCallId: toolCall.id,
|
|
59
100
|
toolCallType: toolCall.type,
|
|
60
|
-
toolName
|
|
101
|
+
toolName
|
|
61
102
|
};
|
|
62
103
|
const completionToolResult = {
|
|
63
104
|
args: parsedArgs,
|
|
64
105
|
result,
|
|
65
106
|
toolCallId: toolCall.id,
|
|
66
|
-
toolName
|
|
107
|
+
toolName
|
|
67
108
|
};
|
|
68
109
|
const message = {
|
|
69
110
|
content: result,
|
|
@@ -77,4 +118,25 @@ const executeTool = async ({ abortSignal, messages, toolCall, tools }) => {
|
|
|
77
118
|
};
|
|
78
119
|
};
|
|
79
120
|
|
|
80
|
-
|
|
121
|
+
const resolveStepOptions = async ({ messages, model, prepareStep, stepNumber, steps, toolChoice }) => {
|
|
122
|
+
const prepared = prepareStep == null ? void 0 : await prepareStep({
|
|
123
|
+
messages: structuredClone(messages),
|
|
124
|
+
model,
|
|
125
|
+
stepNumber,
|
|
126
|
+
steps: structuredClone(steps)
|
|
127
|
+
});
|
|
128
|
+
return {
|
|
129
|
+
messages: prepared?.messages != null ? structuredClone(prepared.messages) : messages,
|
|
130
|
+
model: prepared?.model ?? model,
|
|
131
|
+
toolChoice: prepared?.toolChoice ?? toolChoice
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const and = (...conditions) => (context) => conditions.every((condition) => condition(context));
|
|
136
|
+
const or = (...conditions) => (context) => conditions.some((condition) => condition(context));
|
|
137
|
+
const not = (condition) => (context) => !condition(context);
|
|
138
|
+
const stepCountAtLeast = (count) => ({ steps }) => steps.length >= count;
|
|
139
|
+
const hasToolCall = (name) => ({ step }) => step.toolCalls.some((toolCall) => name == null || toolCall.toolName === name);
|
|
140
|
+
const shouldStop = (stopWhen, context) => stopWhen(context);
|
|
141
|
+
|
|
142
|
+
export { and, chat, determineStepType, executeTool, hasToolCall, not, or, resolveStepOptions, shouldStop, stepCountAtLeast };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xsai/shared-chat",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0-beta.2",
|
|
5
5
|
"description": "extra-small AI SDK.",
|
|
6
6
|
"author": "Moeru AI",
|
|
7
7
|
"license": "MIT",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@xsai/shared": "~0.
|
|
32
|
+
"@xsai/shared": "~0.5.0-beta.2"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "pkgroll"
|