smoltalk 0.0.37 → 0.0.39
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/message/AssistantMessage.d.ts +9 -1
- package/dist/classes/message/AssistantMessage.js +14 -0
- package/dist/classes/message/index.d.ts +3 -1
- package/dist/client.js +3 -4
- package/dist/clients/anthropic.d.ts +2 -1
- package/dist/clients/anthropic.js +34 -15
- package/dist/clients/baseClient.d.ts +6 -0
- package/dist/clients/baseClient.js +131 -7
- package/dist/clients/google.d.ts +2 -1
- package/dist/clients/google.js +29 -7
- package/dist/clients/ollama.d.ts +2 -1
- package/dist/clients/ollama.js +30 -8
- package/dist/clients/openai.d.ts +2 -1
- package/dist/clients/openai.js +14 -9
- package/dist/clients/openaiResponses.d.ts +2 -1
- package/dist/clients/openaiResponses.js +16 -9
- package/dist/functions.js +24 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/model.d.ts +33 -0
- package/dist/model.js +132 -0
- package/dist/models.d.ts +5 -26
- package/dist/models.js +0 -102
- package/dist/smolError.d.ts +6 -0
- package/dist/smolError.js +12 -0
- package/dist/statelogClient.d.ts +2 -1
- package/dist/strategies/baseStrategy.d.ts +10 -0
- package/dist/strategies/baseStrategy.js +20 -0
- package/dist/strategies/fallbackStrategy.d.ts +10 -0
- package/dist/strategies/fallbackStrategy.js +48 -0
- package/dist/strategies/idStrategy.d.ts +10 -0
- package/dist/strategies/idStrategy.js +22 -0
- package/dist/strategies/index.d.ts +11 -0
- package/dist/strategies/index.js +40 -0
- package/dist/strategies/raceStrategy.d.ts +9 -0
- package/dist/strategies/raceStrategy.js +37 -0
- package/dist/strategies/types.d.ts +31 -0
- package/dist/strategies/types.js +1 -0
- package/dist/types.d.ts +27 -1
- package/package.json +1 -1
package/dist/clients/ollama.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getLogger } from "../logger.js";
|
|
|
4
4
|
import { success, } from "../types.js";
|
|
5
5
|
import { zodToGoogleTool } from "../util/tool.js";
|
|
6
6
|
import { BaseClient } from "./baseClient.js";
|
|
7
|
-
import {
|
|
7
|
+
import { Model } from "../model.js";
|
|
8
8
|
export const DEFAULT_OLLAMA_HOST = "http://localhost:11434";
|
|
9
9
|
export class SmolOllama extends BaseClient {
|
|
10
10
|
logger;
|
|
@@ -13,7 +13,7 @@ export class SmolOllama extends BaseClient {
|
|
|
13
13
|
constructor(config) {
|
|
14
14
|
super(config);
|
|
15
15
|
this.logger = getLogger();
|
|
16
|
-
this.model = config.model;
|
|
16
|
+
this.model = new Model(config.model);
|
|
17
17
|
if (config.ollamaApiKey) {
|
|
18
18
|
this.client = new Ollama({
|
|
19
19
|
host: "https://cloud.ollama.com",
|
|
@@ -29,7 +29,7 @@ export class SmolOllama extends BaseClient {
|
|
|
29
29
|
return this.client;
|
|
30
30
|
}
|
|
31
31
|
getModel() {
|
|
32
|
-
return this.model;
|
|
32
|
+
return this.model.getResolvedModel();
|
|
33
33
|
}
|
|
34
34
|
calculateUsageAndCost(responseData) {
|
|
35
35
|
let usage;
|
|
@@ -42,7 +42,7 @@ export class SmolOllama extends BaseClient {
|
|
|
42
42
|
outputTokens,
|
|
43
43
|
totalTokens: inputTokens + outputTokens,
|
|
44
44
|
};
|
|
45
|
-
const calculatedCost =
|
|
45
|
+
const calculatedCost = this.model.calculateCost(usage);
|
|
46
46
|
if (calculatedCost) {
|
|
47
47
|
cost = calculatedCost;
|
|
48
48
|
}
|
|
@@ -58,7 +58,7 @@ export class SmolOllama extends BaseClient {
|
|
|
58
58
|
});
|
|
59
59
|
const request = {
|
|
60
60
|
messages: messages,
|
|
61
|
-
model: this.
|
|
61
|
+
model: this.getModel(),
|
|
62
62
|
};
|
|
63
63
|
if (tools.length > 0) {
|
|
64
64
|
request.tools = tools.map((t) => ({ type: "function", function: t }));
|
|
@@ -70,8 +70,16 @@ export class SmolOllama extends BaseClient {
|
|
|
70
70
|
Object.assign(request, config.rawAttributes);
|
|
71
71
|
}
|
|
72
72
|
this.logger.debug("Sending request to Ollama:", JSON.stringify(request, null, 2));
|
|
73
|
+
const signal = this.getAbortSignal(config);
|
|
74
|
+
const abortHandler = signal ? () => this.client.abort() : undefined;
|
|
75
|
+
if (signal && abortHandler) {
|
|
76
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
77
|
+
}
|
|
73
78
|
// @ts-ignore
|
|
74
79
|
const result = await this.client.chat(request);
|
|
80
|
+
if (signal && abortHandler) {
|
|
81
|
+
signal.removeEventListener("abort", abortHandler);
|
|
82
|
+
}
|
|
75
83
|
this.logger.debug("Response from Ollama:", JSON.stringify(result, null, 2));
|
|
76
84
|
const output = result.message?.content || null;
|
|
77
85
|
const toolCalls = [];
|
|
@@ -84,7 +92,7 @@ export class SmolOllama extends BaseClient {
|
|
|
84
92
|
// Extract usage and calculate cost
|
|
85
93
|
const { usage, cost } = this.calculateUsageAndCost(result);
|
|
86
94
|
// Return the response, updating the chat history
|
|
87
|
-
return success({ output, toolCalls, usage, cost, model: this.
|
|
95
|
+
return success({ output, toolCalls, usage, cost, model: this.getModel() });
|
|
88
96
|
}
|
|
89
97
|
async *_textStream(config) {
|
|
90
98
|
const messages = config.messages.map((msg) => msg.toOpenAIMessage());
|
|
@@ -95,7 +103,7 @@ export class SmolOllama extends BaseClient {
|
|
|
95
103
|
});
|
|
96
104
|
const request = {
|
|
97
105
|
messages: messages,
|
|
98
|
-
model: this.
|
|
106
|
+
model: this.getModel(),
|
|
99
107
|
stream: true,
|
|
100
108
|
};
|
|
101
109
|
if (tools.length > 0) {
|
|
@@ -108,6 +116,11 @@ export class SmolOllama extends BaseClient {
|
|
|
108
116
|
Object.assign(request, config.rawAttributes);
|
|
109
117
|
}
|
|
110
118
|
this.logger.debug("Sending streaming request to Ollama:", JSON.stringify(request, null, 2));
|
|
119
|
+
const signal = this.getAbortSignal(config);
|
|
120
|
+
const abortHandler = signal ? () => this.client.abort() : undefined;
|
|
121
|
+
if (signal && abortHandler) {
|
|
122
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
123
|
+
}
|
|
111
124
|
// @ts-ignore
|
|
112
125
|
const stream = await this.client.chat(request);
|
|
113
126
|
let content = "";
|
|
@@ -148,6 +161,9 @@ export class SmolOllama extends BaseClient {
|
|
|
148
161
|
}
|
|
149
162
|
}
|
|
150
163
|
}
|
|
164
|
+
if (signal && abortHandler) {
|
|
165
|
+
signal.removeEventListener("abort", abortHandler);
|
|
166
|
+
}
|
|
151
167
|
this.logger.debug("Streaming response completed from Ollama");
|
|
152
168
|
// Extract usage from the last chunk
|
|
153
169
|
if (lastChunk) {
|
|
@@ -164,7 +180,13 @@ export class SmolOllama extends BaseClient {
|
|
|
164
180
|
}
|
|
165
181
|
yield {
|
|
166
182
|
type: "done",
|
|
167
|
-
result: {
|
|
183
|
+
result: {
|
|
184
|
+
output: content || null,
|
|
185
|
+
toolCalls,
|
|
186
|
+
usage,
|
|
187
|
+
cost,
|
|
188
|
+
model: this.getModel(),
|
|
189
|
+
},
|
|
168
190
|
};
|
|
169
191
|
}
|
|
170
192
|
}
|
package/dist/clients/openai.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { BaseClientConfig, PromptConfig, PromptResult, Result, SmolClient, StreamChunk } from "../types.js";
|
|
3
3
|
import { BaseClient } from "./baseClient.js";
|
|
4
|
+
import { ModelName } from "../models.js";
|
|
4
5
|
export type SmolOpenAiConfig = BaseClientConfig;
|
|
5
6
|
export declare class SmolOpenAi extends BaseClient implements SmolClient {
|
|
6
7
|
private client;
|
|
@@ -8,7 +9,7 @@ export declare class SmolOpenAi extends BaseClient implements SmolClient {
|
|
|
8
9
|
private model;
|
|
9
10
|
constructor(config: SmolOpenAiConfig);
|
|
10
11
|
getClient(): OpenAI;
|
|
11
|
-
getModel():
|
|
12
|
+
getModel(): ModelName;
|
|
12
13
|
private calculateUsageAndCost;
|
|
13
14
|
private buildRequest;
|
|
14
15
|
_textSync(config: PromptConfig): Promise<Result<PromptResult>>;
|
package/dist/clients/openai.js
CHANGED
|
@@ -5,7 +5,7 @@ import { isFunctionToolCall } from "../util.js";
|
|
|
5
5
|
import { getLogger } from "../logger.js";
|
|
6
6
|
import { BaseClient } from "./baseClient.js";
|
|
7
7
|
import { zodToOpenAITool } from "../util/tool.js";
|
|
8
|
-
import {
|
|
8
|
+
import { Model } from "../model.js";
|
|
9
9
|
export class SmolOpenAi extends BaseClient {
|
|
10
10
|
client;
|
|
11
11
|
logger;
|
|
@@ -17,13 +17,13 @@ export class SmolOpenAi extends BaseClient {
|
|
|
17
17
|
}
|
|
18
18
|
this.client = new OpenAI({ apiKey: config.openAiApiKey });
|
|
19
19
|
this.logger = getLogger();
|
|
20
|
-
this.model = config.model;
|
|
20
|
+
this.model = new Model(config.model);
|
|
21
21
|
}
|
|
22
22
|
getClient() {
|
|
23
23
|
return this.client;
|
|
24
24
|
}
|
|
25
25
|
getModel() {
|
|
26
|
-
return this.model;
|
|
26
|
+
return this.model.getResolvedModel();
|
|
27
27
|
}
|
|
28
28
|
calculateUsageAndCost(usageData) {
|
|
29
29
|
let usage;
|
|
@@ -35,7 +35,7 @@ export class SmolOpenAi extends BaseClient {
|
|
|
35
35
|
cachedInputTokens: usageData.prompt_tokens_details?.cached_tokens,
|
|
36
36
|
totalTokens: usageData.total_tokens,
|
|
37
37
|
};
|
|
38
|
-
const calculatedCost =
|
|
38
|
+
const calculatedCost = this.model.calculateCost(usage);
|
|
39
39
|
if (calculatedCost) {
|
|
40
40
|
cost = calculatedCost;
|
|
41
41
|
}
|
|
@@ -45,13 +45,16 @@ export class SmolOpenAi extends BaseClient {
|
|
|
45
45
|
buildRequest(config) {
|
|
46
46
|
const messages = config.messages.map((msg) => msg.toOpenAIMessage());
|
|
47
47
|
const request = {
|
|
48
|
-
model: this.
|
|
48
|
+
model: this.getModel(),
|
|
49
49
|
messages,
|
|
50
50
|
tools: config.tools?.map((tool) => {
|
|
51
51
|
return zodToOpenAITool(tool.name, tool.schema, {
|
|
52
52
|
description: tool.description,
|
|
53
53
|
});
|
|
54
54
|
}),
|
|
55
|
+
...(config.reasoningEffort && {
|
|
56
|
+
reasoning_effort: config.reasoningEffort,
|
|
57
|
+
}),
|
|
55
58
|
...(config.rawAttributes || {}),
|
|
56
59
|
};
|
|
57
60
|
if (config.responseFormat) {
|
|
@@ -68,10 +71,11 @@ export class SmolOpenAi extends BaseClient {
|
|
|
68
71
|
async _textSync(config) {
|
|
69
72
|
const request = this.buildRequest(config);
|
|
70
73
|
this.logger.debug("Sending request to OpenAI:", JSON.stringify(request, null, 2));
|
|
74
|
+
const signal = this.getAbortSignal(config);
|
|
71
75
|
const completion = await this.client.chat.completions.create({
|
|
72
76
|
...request,
|
|
73
77
|
stream: false,
|
|
74
|
-
});
|
|
78
|
+
}, { ...(signal && { signal }) });
|
|
75
79
|
this.logger.debug("Response from OpenAI:", JSON.stringify(completion, null, 2));
|
|
76
80
|
const message = completion.choices[0].message;
|
|
77
81
|
const output = message.content;
|
|
@@ -94,17 +98,18 @@ export class SmolOpenAi extends BaseClient {
|
|
|
94
98
|
toolCalls,
|
|
95
99
|
usage,
|
|
96
100
|
cost,
|
|
97
|
-
model:
|
|
101
|
+
model: this.getModel(),
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
104
|
async *_textStream(config) {
|
|
101
105
|
const request = this.buildRequest(config);
|
|
102
106
|
this.logger.debug("Sending streaming request to OpenAI:", JSON.stringify(request, null, 2));
|
|
107
|
+
const signal = this.getAbortSignal(config);
|
|
103
108
|
const completion = await this.client.chat.completions.create({
|
|
104
109
|
...request,
|
|
105
110
|
stream: true,
|
|
106
111
|
stream_options: { include_usage: true },
|
|
107
|
-
});
|
|
112
|
+
}, { ...(signal && { signal }) });
|
|
108
113
|
let content = "";
|
|
109
114
|
const toolCallsMap = new Map();
|
|
110
115
|
let usage;
|
|
@@ -159,7 +164,7 @@ export class SmolOpenAi extends BaseClient {
|
|
|
159
164
|
toolCalls,
|
|
160
165
|
usage,
|
|
161
166
|
cost,
|
|
162
|
-
model:
|
|
167
|
+
model: this.getModel(),
|
|
163
168
|
},
|
|
164
169
|
};
|
|
165
170
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { BaseClientConfig, PromptConfig, PromptResult, Result, SmolClient, StreamChunk } from "../types.js";
|
|
3
3
|
import { BaseClient } from "./baseClient.js";
|
|
4
|
+
import { ModelName } from "../models.js";
|
|
4
5
|
export type SmolOpenAiResponsesConfig = BaseClientConfig;
|
|
5
6
|
export declare class SmolOpenAiResponses extends BaseClient implements SmolClient {
|
|
6
7
|
private client;
|
|
@@ -8,7 +9,7 @@ export declare class SmolOpenAiResponses extends BaseClient implements SmolClien
|
|
|
8
9
|
private model;
|
|
9
10
|
constructor(config: SmolOpenAiResponsesConfig);
|
|
10
11
|
getClient(): OpenAI;
|
|
11
|
-
getModel():
|
|
12
|
+
getModel(): ModelName;
|
|
12
13
|
private convertMessages;
|
|
13
14
|
private buildRequest;
|
|
14
15
|
private calculateUsageAndCost;
|
|
@@ -4,7 +4,7 @@ import { ToolCall } from "../classes/ToolCall.js";
|
|
|
4
4
|
import { getLogger } from "../logger.js";
|
|
5
5
|
import { BaseClient } from "./baseClient.js";
|
|
6
6
|
import { zodToOpenAIResponsesTool } from "../util/tool.js";
|
|
7
|
-
import {
|
|
7
|
+
import { Model } from "../model.js";
|
|
8
8
|
export class SmolOpenAiResponses extends BaseClient {
|
|
9
9
|
client;
|
|
10
10
|
logger;
|
|
@@ -16,13 +16,13 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
16
16
|
}
|
|
17
17
|
this.client = new OpenAI({ apiKey: config.openAiApiKey });
|
|
18
18
|
this.logger = getLogger();
|
|
19
|
-
this.model = config.model;
|
|
19
|
+
this.model = new Model(config.model);
|
|
20
20
|
}
|
|
21
21
|
getClient() {
|
|
22
22
|
return this.client;
|
|
23
23
|
}
|
|
24
24
|
getModel() {
|
|
25
|
-
return this.model;
|
|
25
|
+
return this.model.getResolvedModel();
|
|
26
26
|
}
|
|
27
27
|
convertMessages(config) {
|
|
28
28
|
let instructions = config.instructions;
|
|
@@ -48,7 +48,7 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
48
48
|
buildRequest(config) {
|
|
49
49
|
const { instructions, input } = this.convertMessages(config);
|
|
50
50
|
const request = {
|
|
51
|
-
model: this.
|
|
51
|
+
model: this.getModel(),
|
|
52
52
|
input,
|
|
53
53
|
};
|
|
54
54
|
if (instructions) {
|
|
@@ -77,6 +77,9 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
77
77
|
},
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
+
if (config.reasoningEffort) {
|
|
81
|
+
request.reasoning = { effort: config.reasoningEffort };
|
|
82
|
+
}
|
|
80
83
|
if (config.rawAttributes) {
|
|
81
84
|
Object.assign(request, config.rawAttributes);
|
|
82
85
|
}
|
|
@@ -92,7 +95,7 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
92
95
|
cachedInputTokens: usageData.input_tokens_details?.cached_tokens,
|
|
93
96
|
totalTokens: usageData.total_tokens,
|
|
94
97
|
};
|
|
95
|
-
const calculatedCost =
|
|
98
|
+
const calculatedCost = this.model.calculateCost(usage);
|
|
96
99
|
if (calculatedCost) {
|
|
97
100
|
cost = calculatedCost;
|
|
98
101
|
}
|
|
@@ -102,10 +105,11 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
102
105
|
async _textSync(config) {
|
|
103
106
|
const request = this.buildRequest(config);
|
|
104
107
|
this.logger.debug("Sending request to OpenAI Responses API:", JSON.stringify(request, null, 2));
|
|
108
|
+
const signal = this.getAbortSignal(config);
|
|
105
109
|
const response = await this.client.responses.create({
|
|
106
110
|
...request,
|
|
107
111
|
stream: false,
|
|
108
|
-
});
|
|
112
|
+
}, { ...(signal && { signal }) });
|
|
109
113
|
this.logger.debug("Response from OpenAI Responses API:", JSON.stringify(response, null, 2));
|
|
110
114
|
const output = response.output_text || null;
|
|
111
115
|
const toolCalls = [];
|
|
@@ -120,13 +124,16 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
120
124
|
toolCalls,
|
|
121
125
|
usage,
|
|
122
126
|
cost,
|
|
123
|
-
model:
|
|
127
|
+
model: this.getModel(),
|
|
124
128
|
});
|
|
125
129
|
}
|
|
126
130
|
async *_textStream(config) {
|
|
127
131
|
const request = this.buildRequest(config);
|
|
128
132
|
this.logger.debug("Sending streaming request to OpenAI Responses API:", JSON.stringify(request, null, 2));
|
|
129
|
-
const
|
|
133
|
+
const signal = this.getAbortSignal(config);
|
|
134
|
+
const stream = this.client.responses.stream(request, {
|
|
135
|
+
...(signal && { signal }),
|
|
136
|
+
});
|
|
130
137
|
let content = "";
|
|
131
138
|
const functionCalls = new Map();
|
|
132
139
|
let usage;
|
|
@@ -206,7 +213,7 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
206
213
|
toolCalls,
|
|
207
214
|
usage,
|
|
208
215
|
cost,
|
|
209
|
-
model:
|
|
216
|
+
model: this.getModel(),
|
|
210
217
|
},
|
|
211
218
|
};
|
|
212
219
|
}
|
package/dist/functions.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { getClient } from "./client.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Model } from "./model.js";
|
|
3
|
+
import { BaseStrategy } from "./strategies/baseStrategy.js";
|
|
4
|
+
import { fromJSON } from "./strategies/index.js";
|
|
5
|
+
function hydrateStrategy(config) {
|
|
6
|
+
if (config.strategy && !(config.strategy instanceof BaseStrategy)) {
|
|
7
|
+
return { ...config, strategy: fromJSON(config.strategy) };
|
|
8
|
+
}
|
|
9
|
+
return config;
|
|
10
|
+
}
|
|
3
11
|
function splitConfig(config) {
|
|
4
12
|
const { openAiApiKey, googleApiKey, ollamaApiKey, anthropicApiKey, ollamaHost, model: rawModel, provider, logLevel, toolLoopDetection, statelog, ...promptConfig } = config;
|
|
5
|
-
const
|
|
13
|
+
const _model = new Model(rawModel);
|
|
14
|
+
const model = _model.getResolvedModel();
|
|
6
15
|
return {
|
|
7
16
|
smolConfig: {
|
|
8
17
|
openAiApiKey,
|
|
@@ -20,17 +29,29 @@ function splitConfig(config) {
|
|
|
20
29
|
};
|
|
21
30
|
}
|
|
22
31
|
export function text(config) {
|
|
32
|
+
config = hydrateStrategy(config);
|
|
33
|
+
if (config.strategy) {
|
|
34
|
+
return config.strategy.text(config);
|
|
35
|
+
}
|
|
23
36
|
const { smolConfig, promptConfig } = splitConfig(config);
|
|
24
37
|
const client = getClient(smolConfig);
|
|
25
38
|
return client.text(promptConfig);
|
|
26
39
|
}
|
|
27
40
|
export function textSync(config) {
|
|
41
|
+
config = hydrateStrategy(config);
|
|
42
|
+
if (config.strategy) {
|
|
43
|
+
return config.strategy.textSync(config);
|
|
44
|
+
}
|
|
28
45
|
const { smolConfig, promptConfig } = splitConfig(config);
|
|
29
46
|
const client = getClient(smolConfig);
|
|
30
47
|
return client.textSync(promptConfig);
|
|
31
48
|
}
|
|
32
49
|
export function textStream(config) {
|
|
33
|
-
|
|
50
|
+
config = hydrateStrategy(config);
|
|
51
|
+
/* if (config.strategy) {
|
|
52
|
+
return (config.strategy as import("./strategies/types.js").Strategy).textStream(config);
|
|
53
|
+
}
|
|
54
|
+
*/ const { smolConfig, promptConfig } = splitConfig(config);
|
|
34
55
|
const client = getClient(smolConfig);
|
|
35
56
|
return client.textStream(promptConfig);
|
|
36
57
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from "./client.js";
|
|
2
2
|
export * from "./types.js";
|
|
3
3
|
export * from "./models.js";
|
|
4
|
+
export * from "./model.js";
|
|
4
5
|
export * from "./smolError.js";
|
|
5
6
|
export * from "./util.js";
|
|
6
7
|
export * from "./classes/message/index.js";
|
|
7
8
|
export * from "./functions.js";
|
|
8
9
|
export * from "./classes/ToolCall.js";
|
|
10
|
+
export * from "./strategies/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from "./client.js";
|
|
2
2
|
export * from "./types.js";
|
|
3
3
|
export * from "./models.js";
|
|
4
|
+
export * from "./model.js";
|
|
4
5
|
export * from "./smolError.js";
|
|
5
6
|
export * from "./util.js";
|
|
6
7
|
export * from "./classes/message/index.js";
|
|
7
8
|
export * from "./functions.js";
|
|
8
9
|
export * from "./classes/ToolCall.js";
|
|
10
|
+
export * from "./strategies/index.js";
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ModelName, Provider, TextModel } from "./models.js";
|
|
2
|
+
import { ModelLike } from "./types.js";
|
|
3
|
+
export type Optimization = "speed" | "reasoning" | "cost" | "large-context";
|
|
4
|
+
export type ModelConfig = {
|
|
5
|
+
optimizeFor: Optimization[];
|
|
6
|
+
providers: Provider[];
|
|
7
|
+
limit?: {
|
|
8
|
+
cost?: number;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export declare class Model {
|
|
12
|
+
private model;
|
|
13
|
+
private resolvedModel;
|
|
14
|
+
constructor(model: ModelName | ModelConfig);
|
|
15
|
+
getModel(): ModelName | ModelConfig;
|
|
16
|
+
getResolvedModel(): ModelName;
|
|
17
|
+
isModelConfig(model: ModelName | ModelConfig): model is ModelConfig;
|
|
18
|
+
resolveModel(models?: readonly TextModel[]): ModelName;
|
|
19
|
+
private getRawMetric;
|
|
20
|
+
private isLowerBetter;
|
|
21
|
+
calculateCost(usage: {
|
|
22
|
+
inputTokens: number;
|
|
23
|
+
outputTokens: number;
|
|
24
|
+
cachedInputTokens?: number;
|
|
25
|
+
}): {
|
|
26
|
+
inputCost: number;
|
|
27
|
+
outputCost: number;
|
|
28
|
+
cachedInputCost?: number;
|
|
29
|
+
totalCost: number;
|
|
30
|
+
currency: string;
|
|
31
|
+
} | null;
|
|
32
|
+
static create(model: ModelLike): Model;
|
|
33
|
+
}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { getModel, isTextModel, textModels, } from "./models.js";
|
|
2
|
+
import { SmolError } from "./smolError.js";
|
|
3
|
+
import { round } from "./util.js";
|
|
4
|
+
const WEIGHTS = {
|
|
5
|
+
1: [1],
|
|
6
|
+
2: [0.6, 0.4],
|
|
7
|
+
3: [0.5, 0.3, 0.2],
|
|
8
|
+
4: [0.4, 0.3, 0.2, 0.1],
|
|
9
|
+
};
|
|
10
|
+
export class Model {
|
|
11
|
+
model;
|
|
12
|
+
resolvedModel;
|
|
13
|
+
constructor(model) {
|
|
14
|
+
this.model = model;
|
|
15
|
+
this.resolvedModel = this.resolveModel();
|
|
16
|
+
}
|
|
17
|
+
getModel() {
|
|
18
|
+
return this.model;
|
|
19
|
+
}
|
|
20
|
+
getResolvedModel() {
|
|
21
|
+
return this.resolvedModel;
|
|
22
|
+
}
|
|
23
|
+
isModelConfig(model) {
|
|
24
|
+
return typeof model === "object" && "optimizeFor" in model;
|
|
25
|
+
}
|
|
26
|
+
resolveModel(models = textModels) {
|
|
27
|
+
if (!this.isModelConfig(this.model)) {
|
|
28
|
+
const modelName = this.model;
|
|
29
|
+
const model = getModel(modelName);
|
|
30
|
+
if (!model) {
|
|
31
|
+
throw new SmolError(`Model ${modelName} is not recognized. Please specify a known model or a valid ModelConfig.`);
|
|
32
|
+
}
|
|
33
|
+
return modelName;
|
|
34
|
+
}
|
|
35
|
+
const model = this.model;
|
|
36
|
+
let candidates = models.filter((m) => model.providers.includes(m.provider) &&
|
|
37
|
+
!("disabled" in m && m.disabled));
|
|
38
|
+
if (model.limit?.cost !== undefined) {
|
|
39
|
+
candidates = candidates.filter((m) => {
|
|
40
|
+
const cost = (m.inputTokenCost ?? 0) + (m.outputTokenCost ?? 0);
|
|
41
|
+
return cost <= model.limit.cost;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (candidates.length === 0) {
|
|
45
|
+
throw new SmolError("No models available for providers: " +
|
|
46
|
+
model.providers.join(", ") +
|
|
47
|
+
". Check that the providers have non-disabled models.");
|
|
48
|
+
}
|
|
49
|
+
if (candidates.length === 1) {
|
|
50
|
+
return candidates[0].modelName;
|
|
51
|
+
}
|
|
52
|
+
const optimizations = model.optimizeFor;
|
|
53
|
+
const weights = WEIGHTS[optimizations.length] ?? WEIGHTS[4];
|
|
54
|
+
const scores = new Map();
|
|
55
|
+
for (const c of candidates) {
|
|
56
|
+
scores.set(c.modelName, 0);
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < optimizations.length; i++) {
|
|
59
|
+
const opt = optimizations[i];
|
|
60
|
+
const weight = weights[i];
|
|
61
|
+
const rawValues = candidates.map((c) => this.getRawMetric(c, opt));
|
|
62
|
+
const min = Math.min(...rawValues);
|
|
63
|
+
const max = Math.max(...rawValues);
|
|
64
|
+
const range = max - min;
|
|
65
|
+
for (let j = 0; j < candidates.length; j++) {
|
|
66
|
+
const raw = rawValues[j];
|
|
67
|
+
let normalized;
|
|
68
|
+
if (range === 0) {
|
|
69
|
+
normalized = 0;
|
|
70
|
+
}
|
|
71
|
+
else if (this.isLowerBetter(opt)) {
|
|
72
|
+
normalized = (raw - min) / range;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
normalized = (max - raw) / range;
|
|
76
|
+
}
|
|
77
|
+
scores.set(candidates[j].modelName, scores.get(candidates[j].modelName) + weight * normalized);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
let bestModel = candidates[0];
|
|
81
|
+
let bestScore = scores.get(candidates[0].modelName);
|
|
82
|
+
for (let i = 1; i < candidates.length; i++) {
|
|
83
|
+
const score = scores.get(candidates[i].modelName);
|
|
84
|
+
if (score < bestScore) {
|
|
85
|
+
bestScore = score;
|
|
86
|
+
bestModel = candidates[i];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return bestModel.modelName;
|
|
90
|
+
}
|
|
91
|
+
getRawMetric(model, optimization) {
|
|
92
|
+
const m = model;
|
|
93
|
+
switch (optimization) {
|
|
94
|
+
case "cost":
|
|
95
|
+
return (m.inputTokenCost ?? 0) + (m.outputTokenCost ?? 0);
|
|
96
|
+
case "speed":
|
|
97
|
+
return m.outputTokensPerSecond ?? 0;
|
|
98
|
+
case "reasoning":
|
|
99
|
+
return (m.inputTokenCost ?? 0) + (m.outputTokenCost ?? 0);
|
|
100
|
+
case "large-context":
|
|
101
|
+
return m.maxInputTokens;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
isLowerBetter(optimization) {
|
|
105
|
+
return optimization === "cost";
|
|
106
|
+
}
|
|
107
|
+
calculateCost(usage) {
|
|
108
|
+
const model = getModel(this.getResolvedModel());
|
|
109
|
+
if (!model || !isTextModel(model)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const inputCost = round((usage.inputTokens * (model.inputTokenCost || 0)) / 1_000_000, 2);
|
|
113
|
+
const outputCost = round((usage.outputTokens * (model.outputTokenCost || 0)) / 1_000_000, 2);
|
|
114
|
+
const cachedInputCost = usage.cachedInputTokens && model.cachedInputTokenCost
|
|
115
|
+
? round((usage.cachedInputTokens * model.cachedInputTokenCost) / 1_000_000, 2)
|
|
116
|
+
: undefined;
|
|
117
|
+
const totalCost = round(inputCost + outputCost + (cachedInputCost || 0), 2);
|
|
118
|
+
return {
|
|
119
|
+
inputCost,
|
|
120
|
+
outputCost,
|
|
121
|
+
cachedInputCost,
|
|
122
|
+
totalCost,
|
|
123
|
+
currency: "USD",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
static create(model) {
|
|
127
|
+
if (model instanceof Model) {
|
|
128
|
+
return model;
|
|
129
|
+
}
|
|
130
|
+
return new Model(model);
|
|
131
|
+
}
|
|
132
|
+
}
|
package/dist/models.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export type EmbeddingsModel = {
|
|
|
30
30
|
modelName: string;
|
|
31
31
|
tokenCost?: number;
|
|
32
32
|
};
|
|
33
|
-
export type
|
|
33
|
+
export type ModelType = SpeechToTextModel | TextModel | EmbeddingsModel | ImageModel;
|
|
34
34
|
export declare const speechToTextModels: readonly [{
|
|
35
35
|
readonly type: "speech-to-text";
|
|
36
36
|
readonly modelName: "whisper-local";
|
|
@@ -805,28 +805,7 @@ export declare function getModel(modelName: ModelName): {
|
|
|
805
805
|
readonly description: "High-fidelity image generation with reasoning-enhanced composition. Supports legible text rendering, complex multi-turn editing, and character consistency using up to 14 reference inputs.";
|
|
806
806
|
readonly costPerImage: 0.05;
|
|
807
807
|
} | undefined;
|
|
808
|
-
export declare function isImageModel(model:
|
|
809
|
-
export declare function isTextModel(model:
|
|
810
|
-
export declare function isSpeechToTextModel(model:
|
|
811
|
-
export declare function isEmbeddingsModel(model:
|
|
812
|
-
export type Optimization = "speed" | "accuracy" | "cost" | "large-context";
|
|
813
|
-
export type ModelConfig = {
|
|
814
|
-
optimizeFor: Optimization[];
|
|
815
|
-
providers: Provider[];
|
|
816
|
-
limit?: {
|
|
817
|
-
cost?: number;
|
|
818
|
-
};
|
|
819
|
-
};
|
|
820
|
-
export declare function isModelConfig(model: ModelName | ModelConfig): model is ModelConfig;
|
|
821
|
-
export declare function pickModel(config: ModelConfig, models?: readonly TextModel[]): TextModelName;
|
|
822
|
-
export declare function calculateCost(modelName: ModelName, usage: {
|
|
823
|
-
inputTokens: number;
|
|
824
|
-
outputTokens: number;
|
|
825
|
-
cachedInputTokens?: number;
|
|
826
|
-
}): {
|
|
827
|
-
inputCost: number;
|
|
828
|
-
outputCost: number;
|
|
829
|
-
cachedInputCost?: number;
|
|
830
|
-
totalCost: number;
|
|
831
|
-
currency: string;
|
|
832
|
-
} | null;
|
|
808
|
+
export declare function isImageModel(model: ModelType): model is ImageModel;
|
|
809
|
+
export declare function isTextModel(model: ModelType): model is TextModel;
|
|
810
|
+
export declare function isSpeechToTextModel(model: ModelType): model is SpeechToTextModel;
|
|
811
|
+
export declare function isEmbeddingsModel(model: ModelType): model is EmbeddingsModel;
|