smoltalk 0.4.0 → 0.4.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/classes/ToolCall.js +4 -3
- package/dist/classes/message/AssistantMessage.js +5 -3
- package/dist/classes/message/DeveloperMessage.js +5 -3
- package/dist/classes/message/SystemMessage.js +5 -3
- package/dist/classes/message/ToolMessage.js +4 -3
- package/dist/classes/message/UserMessage.js +5 -3
- package/dist/clients/anthropic.d.ts +7 -0
- package/dist/clients/anthropic.js +64 -22
- package/dist/clients/google.d.ts +1 -0
- package/dist/clients/google.js +16 -17
- package/dist/clients/ollama.d.ts +1 -0
- package/dist/clients/ollama.js +17 -6
- package/dist/clients/openai.js +6 -3
- package/dist/clients/openaiResponses.js +6 -3
- package/dist/models.d.ts +46 -24
- package/dist/models.js +18 -12
- package/dist/smolError.d.ts +78 -5
- package/dist/smolError.js +134 -9
- package/dist/util/httpError.d.ts +23 -0
- package/dist/util/httpError.js +237 -0
- package/package.json +1 -1
package/dist/classes/ToolCall.js
CHANGED
|
@@ -47,9 +47,10 @@ export class ToolCall {
|
|
|
47
47
|
static fromJSON(json) {
|
|
48
48
|
const result = ToolCallJSONSchema.safeParse(json);
|
|
49
49
|
if (!result.success) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
const logger = getLogger();
|
|
51
|
+
logger.error("Failed to parse ToolCall:", z.prettifyError(result.error));
|
|
52
|
+
// Raw payload can contain tool-call arguments (often secrets) — only at debug level.
|
|
53
|
+
logger.debug("ToolCall payload that failed to parse:", JSON.stringify(json, null, 2));
|
|
53
54
|
throw new Error("Failed to parse ToolCall");
|
|
54
55
|
}
|
|
55
56
|
return new ToolCall(result.data.id, result.data.name, result.data.arguments);
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { BaseMessage } from "./BaseMessage.js";
|
|
3
3
|
import { CostEstimateSchema, TextPartSchema, ThinkingBlockSchema, TokenUsageSchema, } from "../../types.js";
|
|
4
4
|
import { ToolCall, ToolCallJSONSchema } from "../ToolCall.js";
|
|
5
|
+
import { getLogger } from "../../util/logger.js";
|
|
5
6
|
export const AssistantMessageJSONSchema = z.object({
|
|
6
7
|
role: z.literal("assistant"),
|
|
7
8
|
content: z.union([z.string(), z.array(TextPartSchema), z.null()]),
|
|
@@ -86,9 +87,10 @@ export class AssistantMessage extends BaseMessage {
|
|
|
86
87
|
static fromJSON(json) {
|
|
87
88
|
const result = AssistantMessageJSONSchema.safeParse(json);
|
|
88
89
|
if (!result.success) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
const logger = getLogger();
|
|
91
|
+
logger.error("Failed to parse AssistantMessage:", z.prettifyError(result.error));
|
|
92
|
+
// Raw payload can contain assistant content / tool args — only at debug level.
|
|
93
|
+
logger.debug("AssistantMessage payload that failed to parse:", JSON.stringify(json, null, 2));
|
|
92
94
|
throw new Error("Failed to parse AssistantMessage");
|
|
93
95
|
}
|
|
94
96
|
return new AssistantMessage(result.data.content, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BaseMessage } from "./BaseMessage.js";
|
|
3
3
|
import { TextPartSchema } from "../../types.js";
|
|
4
|
+
import { getLogger } from "../../util/logger.js";
|
|
4
5
|
export const DeveloperMessageJSONSchema = z.object({
|
|
5
6
|
role: z.literal("developer"),
|
|
6
7
|
content: z.union([z.string(), z.array(TextPartSchema)]),
|
|
@@ -43,9 +44,10 @@ export class DeveloperMessage extends BaseMessage {
|
|
|
43
44
|
static fromJSON(json) {
|
|
44
45
|
const result = DeveloperMessageJSONSchema.safeParse(json);
|
|
45
46
|
if (!result.success) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const logger = getLogger();
|
|
48
|
+
logger.error("Failed to parse DeveloperMessage:", z.prettifyError(result.error));
|
|
49
|
+
// Raw payload can contain developer-message content — only at debug level.
|
|
50
|
+
logger.debug("DeveloperMessage payload that failed to parse:", JSON.stringify(json, null, 2));
|
|
49
51
|
throw new Error("Failed to parse DeveloperMessage");
|
|
50
52
|
}
|
|
51
53
|
return new DeveloperMessage(result.data.content, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BaseMessage } from "./BaseMessage.js";
|
|
3
3
|
import { TextPartSchema } from "../../types.js";
|
|
4
|
+
import { getLogger } from "../../util/logger.js";
|
|
4
5
|
export const SystemMessageJSONSchema = z.object({
|
|
5
6
|
role: z.literal("system"),
|
|
6
7
|
content: z.union([z.string(), z.array(TextPartSchema)]),
|
|
@@ -43,9 +44,10 @@ export class SystemMessage extends BaseMessage {
|
|
|
43
44
|
static fromJSON(json) {
|
|
44
45
|
const result = SystemMessageJSONSchema.safeParse(json);
|
|
45
46
|
if (!result.success) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const logger = getLogger();
|
|
48
|
+
logger.error("Failed to parse SystemMessage:", z.prettifyError(result.error));
|
|
49
|
+
// Raw payload can contain system prompt content — only at debug level.
|
|
50
|
+
logger.debug("SystemMessage payload that failed to parse:", JSON.stringify(json, null, 2));
|
|
49
51
|
throw new Error("Failed to parse SystemMessage");
|
|
50
52
|
}
|
|
51
53
|
return new SystemMessage(result.data.content, {
|
|
@@ -52,9 +52,10 @@ export class ToolMessage extends BaseMessage {
|
|
|
52
52
|
static fromJSON(json) {
|
|
53
53
|
const result = ToolMessageJSONSchema.safeParse(json);
|
|
54
54
|
if (!result.success) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const logger = getLogger();
|
|
56
|
+
logger.error("Failed to parse ToolMessage:", z.prettifyError(result.error));
|
|
57
|
+
// Raw payload can contain tool results (file paths, secrets) — only at debug level.
|
|
58
|
+
logger.debug("ToolMessage payload that failed to parse:", JSON.stringify(json, null, 2));
|
|
58
59
|
throw new Error("Failed to parse ToolMessage");
|
|
59
60
|
}
|
|
60
61
|
const TextPartArraySchema = z.array(TextPartSchema);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BaseMessage } from "./BaseMessage.js";
|
|
3
|
+
import { getLogger } from "../../util/logger.js";
|
|
3
4
|
export const UserMessageJSONSchema = z.object({
|
|
4
5
|
role: z.literal("user"),
|
|
5
6
|
content: z.string(),
|
|
@@ -42,9 +43,10 @@ export class UserMessage extends BaseMessage {
|
|
|
42
43
|
static fromJSON(json) {
|
|
43
44
|
const result = UserMessageJSONSchema.safeParse(json);
|
|
44
45
|
if (!result.success) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const logger = getLogger();
|
|
47
|
+
logger.error("Failed to parse UserMessage:", z.prettifyError(result.error));
|
|
48
|
+
// Raw payload can contain user prompts / PII — only at debug level.
|
|
49
|
+
logger.debug("UserMessage payload that failed to parse:", JSON.stringify(json, null, 2));
|
|
48
50
|
throw new Error("Failed to parse UserMessage");
|
|
49
51
|
}
|
|
50
52
|
return new UserMessage(result.data.content, {
|
|
@@ -12,6 +12,13 @@ export declare class SmolAnthropic extends BaseClient implements SmolClient {
|
|
|
12
12
|
getModel(): ModelName;
|
|
13
13
|
private calculateUsageAndCost;
|
|
14
14
|
private buildRequest;
|
|
15
|
+
/**
|
|
16
|
+
* Translate `thinking` / `reasoningEffort` into the correct Anthropic
|
|
17
|
+
* parameters for this model. Callers shouldn't have to know whether a model
|
|
18
|
+
* speaks the legacy `budget_tokens` API or the newer adaptive + effort API —
|
|
19
|
+
* that's exactly the vendor difference this package smooths over.
|
|
20
|
+
*/
|
|
21
|
+
private resolveThinking;
|
|
15
22
|
private rethrowAsSmolError;
|
|
16
23
|
_textSync(config: SmolConfig): Promise<Result<PromptResult>>;
|
|
17
24
|
_textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
|
|
@@ -4,10 +4,25 @@ import { SystemMessage, DeveloperMessage } from "../classes/message/index.js";
|
|
|
4
4
|
import { getLogger } from "../util/logger.js";
|
|
5
5
|
import { success, } from "../types.js";
|
|
6
6
|
import { zodToAnthropicTool } from "../util/tool.js";
|
|
7
|
-
import { SmolContentPolicyError, SmolContextWindowExceededError, } from "../smolError.js";
|
|
7
|
+
import { SmolContentPolicyError, SmolContextWindowExceededError, smolErrorForStatus, } from "../smolError.js";
|
|
8
|
+
import { extractHttpErrorFields } from "../util/httpError.js";
|
|
8
9
|
import { BaseClient } from "./baseClient.js";
|
|
10
|
+
import { getModel, isTextModel } from "../models.js";
|
|
9
11
|
import { Model } from "../model.js";
|
|
10
12
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
13
|
+
/**
|
|
14
|
+
* Which thinking API a model speaks. New flagship Anthropic models (Opus 4.7+)
|
|
15
|
+
* reject the legacy `{type: "enabled", budget_tokens}` form with a 400, so we
|
|
16
|
+
* default unknown/unregistered models to "adaptive" (the forward-looking shape)
|
|
17
|
+
* rather than the form that's being phased out.
|
|
18
|
+
*/
|
|
19
|
+
function thinkingStyleFor(modelName) {
|
|
20
|
+
const model = getModel(modelName);
|
|
21
|
+
if (model && isTextModel(model) && model.reasoning?.thinkingStyle) {
|
|
22
|
+
return model.reasoning.thinkingStyle;
|
|
23
|
+
}
|
|
24
|
+
return "adaptive";
|
|
25
|
+
}
|
|
11
26
|
/**
|
|
12
27
|
* Attach ephemeral cache_control breakpoints to (up to) three places:
|
|
13
28
|
* 1. the last tool definition
|
|
@@ -152,49 +167,72 @@ export class SmolAnthropic extends BaseClient {
|
|
|
152
167
|
description: tool.description,
|
|
153
168
|
}))
|
|
154
169
|
: undefined;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
const thinking = config.thinking?.enabled
|
|
161
|
-
? {
|
|
162
|
-
type: "enabled",
|
|
163
|
-
budget_tokens: config.thinking.budgetTokens ?? 5000,
|
|
164
|
-
}
|
|
165
|
-
: config.reasoningEffort
|
|
166
|
-
? {
|
|
167
|
-
type: "enabled",
|
|
168
|
-
budget_tokens: reasoningBudgetMap[config.reasoningEffort],
|
|
169
|
-
}
|
|
170
|
-
: undefined;
|
|
170
|
+
// Normalize the user's provider-agnostic thinking/effort config into the
|
|
171
|
+
// shape this specific model accepts. Both `thinking.enabled` and
|
|
172
|
+
// `reasoningEffort` are treated as a request to think.
|
|
173
|
+
const { thinking, outputConfig } = this.resolveThinking(config);
|
|
171
174
|
const cachingEnabled = config.caching?.enabled !== false;
|
|
172
175
|
const baseRequest = { system, messages: anthropicMessages, tools };
|
|
173
176
|
const finalRequest = cachingEnabled
|
|
174
177
|
? applyCacheBreakpoints(baseRequest)
|
|
175
178
|
: baseRequest;
|
|
176
|
-
return { ...finalRequest, thinking };
|
|
179
|
+
return { ...finalRequest, thinking, outputConfig };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Translate `thinking` / `reasoningEffort` into the correct Anthropic
|
|
183
|
+
* parameters for this model. Callers shouldn't have to know whether a model
|
|
184
|
+
* speaks the legacy `budget_tokens` API or the newer adaptive + effort API —
|
|
185
|
+
* that's exactly the vendor difference this package smooths over.
|
|
186
|
+
*/
|
|
187
|
+
resolveThinking(config) {
|
|
188
|
+
const wantsThinking = config.thinking?.enabled === true;
|
|
189
|
+
const effort = config.reasoningEffort;
|
|
190
|
+
if (!wantsThinking && !effort) {
|
|
191
|
+
return { thinking: undefined, outputConfig: undefined };
|
|
192
|
+
}
|
|
193
|
+
if (thinkingStyleFor(this.getModel()) === "adaptive") {
|
|
194
|
+
// `output_config.effort` controls depth; the budget is irrelevant here.
|
|
195
|
+
return {
|
|
196
|
+
thinking: { type: "adaptive" },
|
|
197
|
+
outputConfig: effort ? { effort } : undefined,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// Legacy budget-based thinking (Haiku 4.5 and older models).
|
|
201
|
+
const reasoningBudgetMap = {
|
|
202
|
+
low: 2048,
|
|
203
|
+
medium: 5000,
|
|
204
|
+
high: 10000,
|
|
205
|
+
};
|
|
206
|
+
const budget_tokens = wantsThinking
|
|
207
|
+
? (config.thinking?.budgetTokens ?? 5000)
|
|
208
|
+
: reasoningBudgetMap[effort];
|
|
209
|
+
return {
|
|
210
|
+
thinking: { type: "enabled", budget_tokens },
|
|
211
|
+
outputConfig: undefined,
|
|
212
|
+
};
|
|
177
213
|
}
|
|
178
214
|
rethrowAsSmolError(error) {
|
|
179
215
|
if (error instanceof Anthropic.APIError) {
|
|
216
|
+
const http = { ...extractHttpErrorFields(error), cause: error };
|
|
180
217
|
const msg = error.message.toLowerCase();
|
|
181
218
|
if (msg.includes("prompt is too long") ||
|
|
182
219
|
msg.includes("context length") ||
|
|
183
220
|
msg.includes("context window") ||
|
|
184
221
|
msg.includes("too many tokens")) {
|
|
185
|
-
throw new SmolContextWindowExceededError(error.message);
|
|
222
|
+
throw new SmolContextWindowExceededError(error.message, http);
|
|
186
223
|
}
|
|
187
224
|
if (msg.includes("content policy") ||
|
|
188
225
|
msg.includes("usage policies") ||
|
|
189
226
|
msg.includes("content filtering") ||
|
|
190
227
|
msg.includes("violates our")) {
|
|
191
|
-
throw new SmolContentPolicyError(error.message);
|
|
228
|
+
throw new SmolContentPolicyError(error.message, http);
|
|
192
229
|
}
|
|
230
|
+
throw smolErrorForStatus(error.message, http);
|
|
193
231
|
}
|
|
194
232
|
throw error;
|
|
195
233
|
}
|
|
196
234
|
async _textSync(config) {
|
|
197
|
-
const { system, messages, tools, thinking } = this.buildRequest(config);
|
|
235
|
+
const { system, messages, tools, thinking, outputConfig } = this.buildRequest(config);
|
|
198
236
|
let debugData = {
|
|
199
237
|
model: this.getModel(),
|
|
200
238
|
max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
@@ -202,6 +240,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
202
240
|
system,
|
|
203
241
|
tools,
|
|
204
242
|
thinking,
|
|
243
|
+
output_config: outputConfig,
|
|
205
244
|
};
|
|
206
245
|
this.logger.debug("Sending request to Anthropic:", debugData);
|
|
207
246
|
this.statelogClient?.promptRequest(debugData);
|
|
@@ -215,6 +254,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
215
254
|
...(system && { system }),
|
|
216
255
|
...(tools && { tools }),
|
|
217
256
|
...(thinking && { thinking }),
|
|
257
|
+
...(outputConfig && { output_config: outputConfig }),
|
|
218
258
|
...(config.temperature !== undefined && {
|
|
219
259
|
temperature: config.temperature,
|
|
220
260
|
}),
|
|
@@ -253,7 +293,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
253
293
|
});
|
|
254
294
|
}
|
|
255
295
|
async *_textStream(config) {
|
|
256
|
-
const { system, messages, tools, thinking } = this.buildRequest(config);
|
|
296
|
+
const { system, messages, tools, thinking, outputConfig } = this.buildRequest(config);
|
|
257
297
|
const streamDebugData = {
|
|
258
298
|
model: this.model,
|
|
259
299
|
max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
@@ -261,6 +301,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
261
301
|
system,
|
|
262
302
|
tools,
|
|
263
303
|
thinking,
|
|
304
|
+
output_config: outputConfig,
|
|
264
305
|
};
|
|
265
306
|
this.logger.debug("Sending streaming request to Anthropic:", streamDebugData);
|
|
266
307
|
this.statelogClient?.promptRequest(streamDebugData);
|
|
@@ -274,6 +315,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
274
315
|
...(system && { system }),
|
|
275
316
|
...(tools && { tools }),
|
|
276
317
|
...(thinking && { thinking }),
|
|
318
|
+
...(outputConfig && { output_config: outputConfig }),
|
|
277
319
|
...(config.temperature !== undefined && {
|
|
278
320
|
temperature: config.temperature,
|
|
279
321
|
}),
|
package/dist/clients/google.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class SmolGoogle extends BaseClient implements SmolClient {
|
|
|
17
17
|
getModel(): ModelName;
|
|
18
18
|
private calculateUsageAndCost;
|
|
19
19
|
private buildRequest;
|
|
20
|
+
private rethrowAsSmolError;
|
|
20
21
|
_textSync(config: SmolConfig): Promise<Result<PromptResult>>;
|
|
21
22
|
__textSync(request: GeneratedRequest): Promise<Result<PromptResult>>;
|
|
22
23
|
_textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
|
package/dist/clients/google.js
CHANGED
|
@@ -3,7 +3,8 @@ import { ToolCall } from "../classes/ToolCall.js";
|
|
|
3
3
|
import { getLogger } from "../util/logger.js";
|
|
4
4
|
import { addCosts, addTokenUsage, success, } from "../types.js";
|
|
5
5
|
import { zodToGoogleTool } from "../util/tool.js";
|
|
6
|
-
import { SmolContentPolicyError, SmolContextWindowExceededError, } from "../smolError.js";
|
|
6
|
+
import { SmolContentPolicyError, SmolContextWindowExceededError, smolErrorForStatus, } from "../smolError.js";
|
|
7
|
+
import { extractHttpErrorFields } from "../util/httpError.js";
|
|
7
8
|
import { sanitizeAttributes } from "../util/util.js";
|
|
8
9
|
import { BaseClient } from "./baseClient.js";
|
|
9
10
|
import { Model } from "../model.js";
|
|
@@ -88,6 +89,18 @@ export class SmolGoogle extends BaseClient {
|
|
|
88
89
|
...sanitizeAttributes(config.rawAttributes),
|
|
89
90
|
};
|
|
90
91
|
}
|
|
92
|
+
rethrowAsSmolError(error) {
|
|
93
|
+
const http = { ...extractHttpErrorFields(error), cause: error };
|
|
94
|
+
const msg = (error.message || "").toLowerCase();
|
|
95
|
+
if (msg.includes("token") &&
|
|
96
|
+
(msg.includes("exceed") || msg.includes("too long") || msg.includes("limit"))) {
|
|
97
|
+
throw new SmolContextWindowExceededError(error.message, http);
|
|
98
|
+
}
|
|
99
|
+
if (http.status !== undefined) {
|
|
100
|
+
throw smolErrorForStatus(error.message, http);
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
91
104
|
async _textSync(config) {
|
|
92
105
|
const signal = this.getAbortSignal(config);
|
|
93
106
|
const request = {
|
|
@@ -180,14 +193,7 @@ export class SmolGoogle extends BaseClient {
|
|
|
180
193
|
result = await this.client.models.generateContent(request);
|
|
181
194
|
}
|
|
182
195
|
catch (error) {
|
|
183
|
-
|
|
184
|
-
if (msg.includes("token") &&
|
|
185
|
-
(msg.includes("exceed") ||
|
|
186
|
-
msg.includes("too long") ||
|
|
187
|
-
msg.includes("limit"))) {
|
|
188
|
-
throw new SmolContextWindowExceededError(error.message);
|
|
189
|
-
}
|
|
190
|
-
throw error;
|
|
196
|
+
this.rethrowAsSmolError(error);
|
|
191
197
|
}
|
|
192
198
|
this.logger.debug("Response from Google Gemini:", JSON.stringify(result, null, 2));
|
|
193
199
|
this.statelogClient?.promptResponse(result);
|
|
@@ -257,14 +263,7 @@ export class SmolGoogle extends BaseClient {
|
|
|
257
263
|
stream = await this.client.models.generateContentStream(request);
|
|
258
264
|
}
|
|
259
265
|
catch (error) {
|
|
260
|
-
|
|
261
|
-
if (msg.includes("token") &&
|
|
262
|
-
(msg.includes("exceed") ||
|
|
263
|
-
msg.includes("too long") ||
|
|
264
|
-
msg.includes("limit"))) {
|
|
265
|
-
throw new SmolContextWindowExceededError(error.message);
|
|
266
|
-
}
|
|
267
|
-
throw error;
|
|
266
|
+
this.rethrowAsSmolError(error);
|
|
268
267
|
}
|
|
269
268
|
let content = "";
|
|
270
269
|
const toolCallsMap = new Map();
|
package/dist/clients/ollama.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class SmolOllama extends BaseClient implements SmolClient {
|
|
|
12
12
|
getClient(): Ollama;
|
|
13
13
|
getModel(): ModelName;
|
|
14
14
|
private calculateUsageAndCost;
|
|
15
|
+
private rethrowAsSmolError;
|
|
15
16
|
_textSync(config: SmolConfig): Promise<Result<PromptResult>>;
|
|
16
17
|
_textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
|
|
17
18
|
}
|
package/dist/clients/ollama.js
CHANGED
|
@@ -5,7 +5,8 @@ import { success, } from "../types.js";
|
|
|
5
5
|
import { zodToGoogleTool } from "../util/tool.js";
|
|
6
6
|
import { sanitizeAttributes } from "../util/util.js";
|
|
7
7
|
import { BaseClient } from "./baseClient.js";
|
|
8
|
-
import { SmolContextWindowExceededError } from "../smolError.js";
|
|
8
|
+
import { SmolContextWindowExceededError, smolErrorForStatus, } from "../smolError.js";
|
|
9
|
+
import { extractHttpErrorFields } from "../util/httpError.js";
|
|
9
10
|
import { Model } from "../model.js";
|
|
10
11
|
export const DEFAULT_OLLAMA_HOST = "http://localhost:11434";
|
|
11
12
|
export class SmolOllama extends BaseClient {
|
|
@@ -51,6 +52,17 @@ export class SmolOllama extends BaseClient {
|
|
|
51
52
|
}
|
|
52
53
|
return { usage, cost };
|
|
53
54
|
}
|
|
55
|
+
rethrowAsSmolError(error) {
|
|
56
|
+
const http = { ...extractHttpErrorFields(error), cause: error };
|
|
57
|
+
const msg = (error.message || "").toLowerCase();
|
|
58
|
+
if (msg.includes("context length") || msg.includes("context window")) {
|
|
59
|
+
throw new SmolContextWindowExceededError(error.message, http);
|
|
60
|
+
}
|
|
61
|
+
if (http.status !== undefined) {
|
|
62
|
+
throw smolErrorForStatus(error.message, http);
|
|
63
|
+
}
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
54
66
|
async _textSync(config) {
|
|
55
67
|
const messages = config.messages.map((msg) => msg.toOpenAIMessage());
|
|
56
68
|
const tools = (config.tools || []).map((tool) => {
|
|
@@ -82,11 +94,7 @@ export class SmolOllama extends BaseClient {
|
|
|
82
94
|
result = await this.client.chat(request);
|
|
83
95
|
}
|
|
84
96
|
catch (error) {
|
|
85
|
-
|
|
86
|
-
if (msg.includes("context length") || msg.includes("context window")) {
|
|
87
|
-
throw new SmolContextWindowExceededError(error.message);
|
|
88
|
-
}
|
|
89
|
-
throw error;
|
|
97
|
+
this.rethrowAsSmolError(error);
|
|
90
98
|
}
|
|
91
99
|
finally {
|
|
92
100
|
if (signal && abortHandler) {
|
|
@@ -201,6 +209,9 @@ export class SmolOllama extends BaseClient {
|
|
|
201
209
|
},
|
|
202
210
|
};
|
|
203
211
|
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.rethrowAsSmolError(error);
|
|
214
|
+
}
|
|
204
215
|
finally {
|
|
205
216
|
if (signal && abortHandler) {
|
|
206
217
|
signal.removeEventListener("abort", abortHandler);
|
package/dist/clients/openai.js
CHANGED
|
@@ -4,7 +4,8 @@ import { ToolCall } from "../classes/ToolCall.js";
|
|
|
4
4
|
import { isFunctionToolCall, sanitizeAttributes } from "../util/util.js";
|
|
5
5
|
import { getLogger } from "../util/logger.js";
|
|
6
6
|
import { BaseClient } from "./baseClient.js";
|
|
7
|
-
import { SmolContentPolicyError, SmolContextWindowExceededError, } from "../smolError.js";
|
|
7
|
+
import { SmolContentPolicyError, SmolContextWindowExceededError, smolErrorForStatus, } from "../smolError.js";
|
|
8
|
+
import { extractHttpErrorFields } from "../util/httpError.js";
|
|
8
9
|
import { zodToOpenAITool } from "../util/tool.js";
|
|
9
10
|
import { Model } from "../model.js";
|
|
10
11
|
export class SmolOpenAi extends BaseClient {
|
|
@@ -74,12 +75,14 @@ export class SmolOpenAi extends BaseClient {
|
|
|
74
75
|
}
|
|
75
76
|
rethrowAsSmolError(error) {
|
|
76
77
|
if (error instanceof OpenAI.APIError) {
|
|
78
|
+
const http = { ...extractHttpErrorFields(error), cause: error };
|
|
77
79
|
if (error.code === "context_length_exceeded") {
|
|
78
|
-
throw new SmolContextWindowExceededError(error.message);
|
|
80
|
+
throw new SmolContextWindowExceededError(error.message, http);
|
|
79
81
|
}
|
|
80
82
|
if (error.code === "content_policy_violation") {
|
|
81
|
-
throw new SmolContentPolicyError(error.message);
|
|
83
|
+
throw new SmolContentPolicyError(error.message, http);
|
|
82
84
|
}
|
|
85
|
+
throw smolErrorForStatus(error.message, http);
|
|
83
86
|
}
|
|
84
87
|
throw error;
|
|
85
88
|
}
|
|
@@ -6,7 +6,8 @@ import { BaseClient } from "./baseClient.js";
|
|
|
6
6
|
import { zodToOpenAIResponsesTool } from "../util/tool.js";
|
|
7
7
|
import { sanitizeAttributes } from "../util/util.js";
|
|
8
8
|
import { Model } from "../model.js";
|
|
9
|
-
import { SmolContentPolicyError, SmolContextWindowExceededError, } from "../smolError.js";
|
|
9
|
+
import { SmolContentPolicyError, SmolContextWindowExceededError, smolErrorForStatus, } from "../smolError.js";
|
|
10
|
+
import { extractHttpErrorFields } from "../util/httpError.js";
|
|
10
11
|
export class SmolOpenAiResponses extends BaseClient {
|
|
11
12
|
client;
|
|
12
13
|
logger;
|
|
@@ -107,12 +108,14 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
107
108
|
}
|
|
108
109
|
rethrowAsSmolError(error) {
|
|
109
110
|
if (error instanceof OpenAI.APIError) {
|
|
111
|
+
const http = { ...extractHttpErrorFields(error), cause: error };
|
|
110
112
|
if (error.code === "context_length_exceeded") {
|
|
111
|
-
throw new SmolContextWindowExceededError(error.message);
|
|
113
|
+
throw new SmolContextWindowExceededError(error.message, http);
|
|
112
114
|
}
|
|
113
115
|
if (error.code === "content_policy_violation") {
|
|
114
|
-
throw new SmolContentPolicyError(error.message);
|
|
116
|
+
throw new SmolContentPolicyError(error.message, http);
|
|
115
117
|
}
|
|
118
|
+
throw smolErrorForStatus(error.message, http);
|
|
116
119
|
}
|
|
117
120
|
throw error;
|
|
118
121
|
}
|