smoltalk 0.14.0 → 0.14.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/README.md +10 -2
- package/dist/clients/google.js +3 -0
- package/dist/clients/ollama.js +6 -0
- package/dist/clients/openai.d.ts +9 -2
- package/dist/clients/openai.js +15 -2
- package/dist/clients/openaiCompat.d.ts +1 -0
- package/dist/clients/openaiCompat.js +8 -0
- package/dist/clients/openrouter.d.ts +2 -3
- package/dist/clients/openrouter.js +3 -5
- package/dist/models.d.ts +57 -13
- package/dist/models.js +66 -15
- package/dist/speech/baseSpeechClient.d.ts +5 -0
- package/dist/speech/mlx.d.ts +23 -0
- package/dist/speech/mlx.js +49 -0
- package/dist/speech/openai.d.ts +3 -0
- package/dist/speech/openai.js +9 -1
- package/dist/speech.d.ts +5 -0
- package/dist/speech.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -250,7 +250,7 @@ model ids aren't in the smoltalk registry.
|
|
|
250
250
|
|
|
251
251
|
| `provider:` | What it is | Required config | Cost source |
|
|
252
252
|
|-------------|------------|-----------------|-------------|
|
|
253
|
-
| `"openrouter"` | OpenRouter.ai aggregator | `apiKey.openRouter` (or `OPENROUTER_API_KEY`) | `usage.cost`
|
|
253
|
+
| `"openrouter"` | OpenRouter.ai aggregator | `apiKey.openRouter` (or `OPENROUTER_API_KEY`) | `usage.cost` |
|
|
254
254
|
| `"deepinfra"` | DeepInfra hosted models | `apiKey.deepInfra` (or `DEEPINFRA_API_KEY`) | `usage.estimated_cost` |
|
|
255
255
|
| `"litellm"` | Your own LiteLLM proxy | `apiKey.liteLlm` + `baseUrl.liteLlm` (or `LITELLM_API_KEY` / `LITELLM_BASE_URL`) | `x-litellm-response-cost` header (non-stream only) |
|
|
256
256
|
| `"openai-compat"` | Any OpenAI-shape backend (vLLM, TGI, LM Studio…) | `apiKey.openAiCompat` + `baseUrl.openAiCompat` (or `OPENAI_COMPAT_API_KEY` / `OPENAI_COMPAT_BASE_URL`) | Best-effort: reads `usage.cost`/`estimated_cost`/`cost_usd` if present |
|
|
@@ -561,6 +561,10 @@ that exposes OpenAI-shaped `/audio/*` endpoints, use the generic
|
|
|
561
561
|
**`openai-compat`** provider with `baseUrl` (mirrors the chat client). Anthropic,
|
|
562
562
|
OpenRouter, and Ollama have no audio endpoints and return a `Failure`.
|
|
563
563
|
|
|
564
|
+
`speak()` also supports **`mlx`**: a local MLX speech server at `baseUrl.mlx`,
|
|
565
|
+
`MLX_BASE_URL`, or `http://127.0.0.1:8080/v1`. No API key, no retries, zero
|
|
566
|
+
cost, and `"wav"` by default.
|
|
567
|
+
|
|
564
568
|
```ts
|
|
565
569
|
// example: skip-typecheck
|
|
566
570
|
// Groq STT (OpenAI-compatible; provider inferred from the model)
|
|
@@ -655,7 +659,11 @@ if (result.success) {
|
|
|
655
659
|
`tts-1` and `tts-1-hd` are the only baked-in models in v1. `voice` is
|
|
656
660
|
required. Options: `format` (OpenAI accepts `"mp3"` | `"opus"` | `"aac"` |
|
|
657
661
|
`"flac"` | `"wav"` | `"pcm"`, default `"mp3"`; a custom provider may accept
|
|
658
|
-
other strings) and `
|
|
662
|
+
other strings), `speed`, and `instructions`. `instructions` is free-text
|
|
663
|
+
guidance on how the speech should sound (`"Alarmed and urgent."`). It is passed
|
|
664
|
+
through as-is by the OpenAI-shaped providers (`openai`, `groq`, `openai-compat`,
|
|
665
|
+
`mlx`); whether the model reads it is up to the model (`gpt-4o-mini-tts` does,
|
|
666
|
+
`tts-1` does not). The `google` provider ignores it. Limits are declared per model in the registry —
|
|
659
667
|
for `tts-1`/`tts-1-hd` that's a 4096-code-point input cap, a 0.25–4.0 speed
|
|
660
668
|
range, and the format list above; exceeding any of them returns a `Failure`
|
|
661
669
|
before the request is sent. The returned `audio` is a `Uint8Array` you own —
|
package/dist/clients/google.js
CHANGED
|
@@ -260,6 +260,9 @@ export class SmolGoogle extends BaseClient {
|
|
|
260
260
|
});
|
|
261
261
|
});
|
|
262
262
|
const genConfig = {};
|
|
263
|
+
if (config.maxTokens !== undefined) {
|
|
264
|
+
genConfig.maxOutputTokens = config.maxTokens;
|
|
265
|
+
}
|
|
263
266
|
if (systemParts.length > 0) {
|
|
264
267
|
genConfig.systemInstruction = systemParts.join("\n");
|
|
265
268
|
}
|
package/dist/clients/ollama.js
CHANGED
|
@@ -85,6 +85,9 @@ export class SmolOllama extends BaseClient {
|
|
|
85
85
|
if (config.responseFormat) {
|
|
86
86
|
request.format = responseFormatToJsonSchema(config.responseFormat);
|
|
87
87
|
}
|
|
88
|
+
if (config.maxTokens !== undefined) {
|
|
89
|
+
request.options = { num_predict: config.maxTokens };
|
|
90
|
+
}
|
|
88
91
|
Object.assign(request, sanitizeAttributes(config.rawAttributes));
|
|
89
92
|
this.logger.debug("Sending request to Ollama:", JSON.stringify(redactAttachments(request), null, 2));
|
|
90
93
|
this.statelogClient?.promptRequest(request);
|
|
@@ -151,6 +154,9 @@ export class SmolOllama extends BaseClient {
|
|
|
151
154
|
if (config.responseFormat) {
|
|
152
155
|
request.format = responseFormatToJsonSchema(config.responseFormat);
|
|
153
156
|
}
|
|
157
|
+
if (config.maxTokens !== undefined) {
|
|
158
|
+
request.options = { num_predict: config.maxTokens };
|
|
159
|
+
}
|
|
154
160
|
Object.assign(request, sanitizeAttributes(config.rawAttributes));
|
|
155
161
|
this.logger.debug("Sending streaming request to Ollama:", JSON.stringify(redactAttachments(request), null, 2));
|
|
156
162
|
this.statelogClient?.promptRequest(request);
|
package/dist/clients/openai.d.ts
CHANGED
|
@@ -32,11 +32,18 @@ export declare class SmolOpenAi extends BaseClient implements SmolClient {
|
|
|
32
32
|
protected resolveCostUsd(_usage: any, _rawResponse?: Response): number | undefined;
|
|
33
33
|
/**
|
|
34
34
|
* Extra request body fields injected on every call. Subclasses override to
|
|
35
|
-
* add provider-specific request shapes (e.g. OpenRouter's
|
|
36
|
-
*
|
|
35
|
+
* add provider-specific request shapes (e.g. OpenRouter's
|
|
36
|
+
* `plugins: [{ id: "web" }]` for web search). Merged into the request
|
|
37
37
|
* after the standard params so it can override them.
|
|
38
38
|
*/
|
|
39
39
|
protected buildRequestExtras(_config: SmolConfig): Record<string, unknown>;
|
|
40
|
+
/**
|
|
41
|
+
* The request field carrying `config.maxTokens`. OpenAI deprecated
|
|
42
|
+
* `max_tokens` in favor of `max_completion_tokens`, and its reasoning models
|
|
43
|
+
* reject `max_tokens` outright. Compat servers override this to send
|
|
44
|
+
* `max_tokens`, which is the field they reliably accept.
|
|
45
|
+
*/
|
|
46
|
+
protected maxTokensParam(config: SmolConfig): Record<string, number>;
|
|
40
47
|
/**
|
|
41
48
|
* Extract provider-specific hosted-tool results (e.g. OpenRouter web_search
|
|
42
49
|
* annotations) from a completion. Subclasses override; default returns none.
|
package/dist/clients/openai.js
CHANGED
|
@@ -51,13 +51,25 @@ export class SmolOpenAi extends BaseClient {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Extra request body fields injected on every call. Subclasses override to
|
|
54
|
-
* add provider-specific request shapes (e.g. OpenRouter's
|
|
55
|
-
*
|
|
54
|
+
* add provider-specific request shapes (e.g. OpenRouter's
|
|
55
|
+
* `plugins: [{ id: "web" }]` for web search). Merged into the request
|
|
56
56
|
* after the standard params so it can override them.
|
|
57
57
|
*/
|
|
58
58
|
buildRequestExtras(_config) {
|
|
59
59
|
return {};
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The request field carrying `config.maxTokens`. OpenAI deprecated
|
|
63
|
+
* `max_tokens` in favor of `max_completion_tokens`, and its reasoning models
|
|
64
|
+
* reject `max_tokens` outright. Compat servers override this to send
|
|
65
|
+
* `max_tokens`, which is the field they reliably accept.
|
|
66
|
+
*/
|
|
67
|
+
maxTokensParam(config) {
|
|
68
|
+
if (config.maxTokens === undefined) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
return { max_completion_tokens: config.maxTokens };
|
|
72
|
+
}
|
|
61
73
|
/**
|
|
62
74
|
* Extract provider-specific hosted-tool results (e.g. OpenRouter web_search
|
|
63
75
|
* annotations) from a completion. Subclasses override; default returns none.
|
|
@@ -126,6 +138,7 @@ export class SmolOpenAi extends BaseClient {
|
|
|
126
138
|
...(config.reasoningEffort && {
|
|
127
139
|
reasoning_effort: config.reasoningEffort,
|
|
128
140
|
}),
|
|
141
|
+
...this.maxTokensParam(config),
|
|
129
142
|
...sanitizeAttributes(config.rawAttributes),
|
|
130
143
|
...this.buildRequestExtras(config),
|
|
131
144
|
};
|
|
@@ -30,6 +30,14 @@ export class SmolOpenAiCompat extends SmolOpenAi {
|
|
|
30
30
|
}
|
|
31
31
|
return { apiKey, baseURL };
|
|
32
32
|
}
|
|
33
|
+
// Compat servers (OpenRouter, DeepInfra, LiteLLM, vLLM, mlx_lm.server) all
|
|
34
|
+
// accept `max_tokens`; `max_completion_tokens` support is patchy.
|
|
35
|
+
maxTokensParam(config) {
|
|
36
|
+
if (config.maxTokens === undefined) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
return { max_tokens: config.maxTokens };
|
|
40
|
+
}
|
|
33
41
|
resolveCostUsd(usage) {
|
|
34
42
|
// Try the three common conventions across OpenAI-compatible providers.
|
|
35
43
|
const c = usage?.cost ?? usage?.estimated_cost ?? usage?.cost_usd;
|
|
@@ -5,9 +5,8 @@ import type { SmolConfig, HostedToolResult } from "../types.js";
|
|
|
5
5
|
*
|
|
6
6
|
* - Baked base URL `https://openrouter.ai/api/v1` (override via config.baseUrl.openRouter).
|
|
7
7
|
* - Key: config.apiKey.openRouter or env OPENROUTER_API_KEY.
|
|
8
|
-
* - Cost: reads `usage.cost` (USD). OpenRouter
|
|
9
|
-
*
|
|
10
|
-
* injects automatically via buildRequestExtras.
|
|
8
|
+
* - Cost: reads `usage.cost` (USD). OpenRouter includes this in every
|
|
9
|
+
* response (in the final SSE chunk when streaming); no request flag needed.
|
|
11
10
|
* - Hosted web_search: when config.hostedTools includes "web_search",
|
|
12
11
|
* injects `plugins: [{ id: "web", max_results: 5 }]` and parses
|
|
13
12
|
* message.annotations into a HostedToolResult.
|
|
@@ -6,9 +6,8 @@ import { resolveApiKey, resolveBaseUrl } from "../util/provider.js";
|
|
|
6
6
|
*
|
|
7
7
|
* - Baked base URL `https://openrouter.ai/api/v1` (override via config.baseUrl.openRouter).
|
|
8
8
|
* - Key: config.apiKey.openRouter or env OPENROUTER_API_KEY.
|
|
9
|
-
* - Cost: reads `usage.cost` (USD). OpenRouter
|
|
10
|
-
*
|
|
11
|
-
* injects automatically via buildRequestExtras.
|
|
9
|
+
* - Cost: reads `usage.cost` (USD). OpenRouter includes this in every
|
|
10
|
+
* response (in the final SSE chunk when streaming); no request flag needed.
|
|
12
11
|
* - Hosted web_search: when config.hostedTools includes "web_search",
|
|
13
12
|
* injects `plugins: [{ id: "web", max_results: 5 }]` and parses
|
|
14
13
|
* message.annotations into a HostedToolResult.
|
|
@@ -27,8 +26,7 @@ export class SmolOpenRouter extends SmolOpenAiCompat {
|
|
|
27
26
|
return typeof usage?.cost === "number" ? usage.cost : undefined;
|
|
28
27
|
}
|
|
29
28
|
buildRequestExtras(config) {
|
|
30
|
-
|
|
31
|
-
const extras = { usage: { include: true } };
|
|
29
|
+
const extras = {};
|
|
32
30
|
if (config.hostedTools?.includes("web_search")) {
|
|
33
31
|
extras.plugins = [{ id: "web", max_results: 5 }];
|
|
34
32
|
}
|
package/dist/models.d.ts
CHANGED
|
@@ -176,6 +176,13 @@ export declare const textToSpeechModels: readonly [{
|
|
|
176
176
|
readonly perCharacterCost: 0.00004;
|
|
177
177
|
readonly maxInputChars: 200;
|
|
178
178
|
readonly formats: readonly ["wav"];
|
|
179
|
+
}, {
|
|
180
|
+
readonly type: "text-to-speech";
|
|
181
|
+
readonly modelName: "gemini-3.1-flash-tts-preview";
|
|
182
|
+
readonly provider: "google";
|
|
183
|
+
readonly inputTokenCost: 1;
|
|
184
|
+
readonly outputAudioTokenCost: 20;
|
|
185
|
+
readonly formats: readonly ["pcm", "wav"];
|
|
179
186
|
}, {
|
|
180
187
|
readonly type: "text-to-speech";
|
|
181
188
|
readonly modelName: "gemini-2.5-flash-preview-tts";
|
|
@@ -688,7 +695,7 @@ export declare const textModels: readonly [{
|
|
|
688
695
|
readonly inputTokenCost: 5;
|
|
689
696
|
readonly cachedInputTokenCost: 0.5;
|
|
690
697
|
readonly outputTokenCost: 22.5;
|
|
691
|
-
readonly thresholdTokens:
|
|
698
|
+
readonly thresholdTokens: 272000;
|
|
692
699
|
};
|
|
693
700
|
readonly reasoning: {
|
|
694
701
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh"];
|
|
@@ -778,7 +785,7 @@ export declare const textModels: readonly [{
|
|
|
778
785
|
readonly longContext: {
|
|
779
786
|
readonly inputTokenCost: 60;
|
|
780
787
|
readonly outputTokenCost: 270;
|
|
781
|
-
readonly thresholdTokens:
|
|
788
|
+
readonly thresholdTokens: 272000;
|
|
782
789
|
};
|
|
783
790
|
readonly reasoning: {
|
|
784
791
|
readonly levels: readonly ["medium", "high", "xhigh"];
|
|
@@ -813,7 +820,7 @@ export declare const textModels: readonly [{
|
|
|
813
820
|
readonly inputTokenCost: 10;
|
|
814
821
|
readonly cachedInputTokenCost: 1;
|
|
815
822
|
readonly outputTokenCost: 45;
|
|
816
|
-
readonly thresholdTokens:
|
|
823
|
+
readonly thresholdTokens: 272000;
|
|
817
824
|
};
|
|
818
825
|
readonly reasoning: {
|
|
819
826
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh"];
|
|
@@ -845,7 +852,7 @@ export declare const textModels: readonly [{
|
|
|
845
852
|
readonly longContext: {
|
|
846
853
|
readonly inputTokenCost: 60;
|
|
847
854
|
readonly outputTokenCost: 270;
|
|
848
|
-
readonly thresholdTokens:
|
|
855
|
+
readonly thresholdTokens: 272000;
|
|
849
856
|
};
|
|
850
857
|
readonly reasoning: {
|
|
851
858
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh"];
|
|
@@ -866,6 +873,41 @@ export declare const textModels: readonly [{
|
|
|
866
873
|
readonly structuredOutput: true;
|
|
867
874
|
readonly temperatureSupported: false;
|
|
868
875
|
readonly provider: "openai-responses";
|
|
876
|
+
}, {
|
|
877
|
+
readonly type: "text";
|
|
878
|
+
readonly modelName: "gpt-6-astra";
|
|
879
|
+
readonly description: "GPT-6 Astra is OpenAI's most capable model, built for the hardest end-to-end work: complex reasoning, coding, computer use, research, and document creation. 1M context window. Standard pricing for ≤272K input tokens; prompts above that are billed at 2x input/cache and 1.5x output for the whole request. Knowledge cutoff: April 2026.";
|
|
880
|
+
readonly maxInputTokens: 1050000;
|
|
881
|
+
readonly maxOutputTokens: 128000;
|
|
882
|
+
readonly inputTokenCost: 10;
|
|
883
|
+
readonly cachedInputTokenCost: 1;
|
|
884
|
+
readonly outputTokenCost: 50;
|
|
885
|
+
readonly outputTokensPerSecond: 69;
|
|
886
|
+
readonly longContext: {
|
|
887
|
+
readonly inputTokenCost: 20;
|
|
888
|
+
readonly cachedInputTokenCost: 2;
|
|
889
|
+
readonly outputTokenCost: 75;
|
|
890
|
+
readonly thresholdTokens: 272000;
|
|
891
|
+
};
|
|
892
|
+
readonly reasoning: {
|
|
893
|
+
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
894
|
+
readonly defaultLevel: "medium";
|
|
895
|
+
readonly canDisable: false;
|
|
896
|
+
readonly outputsThinking: false;
|
|
897
|
+
readonly outputsSignatures: false;
|
|
898
|
+
};
|
|
899
|
+
readonly modalities: {
|
|
900
|
+
readonly input: readonly ["text", "image", "pdf"];
|
|
901
|
+
readonly output: readonly ["text"];
|
|
902
|
+
};
|
|
903
|
+
readonly knowledge: "2026-04-30";
|
|
904
|
+
readonly releaseDate: "2026-09-04";
|
|
905
|
+
readonly lastUpdated: "2026-09-04";
|
|
906
|
+
readonly family: "gpt";
|
|
907
|
+
readonly openWeights: false;
|
|
908
|
+
readonly structuredOutput: true;
|
|
909
|
+
readonly temperatureSupported: false;
|
|
910
|
+
readonly provider: "openai";
|
|
869
911
|
}, {
|
|
870
912
|
readonly type: "text";
|
|
871
913
|
readonly modelName: "gpt-5.6-sol";
|
|
@@ -875,12 +917,12 @@ export declare const textModels: readonly [{
|
|
|
875
917
|
readonly inputTokenCost: 4;
|
|
876
918
|
readonly cachedInputTokenCost: 0.4;
|
|
877
919
|
readonly outputTokenCost: 20;
|
|
878
|
-
readonly outputTokensPerSecond:
|
|
920
|
+
readonly outputTokensPerSecond: 77;
|
|
879
921
|
readonly longContext: {
|
|
880
922
|
readonly inputTokenCost: 8;
|
|
881
923
|
readonly cachedInputTokenCost: 0.8;
|
|
882
924
|
readonly outputTokenCost: 30;
|
|
883
|
-
readonly thresholdTokens:
|
|
925
|
+
readonly thresholdTokens: 272000;
|
|
884
926
|
};
|
|
885
927
|
readonly reasoning: {
|
|
886
928
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -910,12 +952,12 @@ export declare const textModels: readonly [{
|
|
|
910
952
|
readonly inputTokenCost: 2;
|
|
911
953
|
readonly cachedInputTokenCost: 0.2;
|
|
912
954
|
readonly outputTokenCost: 12;
|
|
913
|
-
readonly outputTokensPerSecond:
|
|
955
|
+
readonly outputTokensPerSecond: 106;
|
|
914
956
|
readonly longContext: {
|
|
915
957
|
readonly inputTokenCost: 4;
|
|
916
958
|
readonly cachedInputTokenCost: 0.4;
|
|
917
959
|
readonly outputTokenCost: 18;
|
|
918
|
-
readonly thresholdTokens:
|
|
960
|
+
readonly thresholdTokens: 272000;
|
|
919
961
|
};
|
|
920
962
|
readonly reasoning: {
|
|
921
963
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -945,12 +987,12 @@ export declare const textModels: readonly [{
|
|
|
945
987
|
readonly inputTokenCost: 0.2;
|
|
946
988
|
readonly cachedInputTokenCost: 0.02;
|
|
947
989
|
readonly outputTokenCost: 1.2;
|
|
948
|
-
readonly outputTokensPerSecond:
|
|
990
|
+
readonly outputTokensPerSecond: 165;
|
|
949
991
|
readonly longContext: {
|
|
950
992
|
readonly inputTokenCost: 0.4;
|
|
951
993
|
readonly cachedInputTokenCost: 0.04;
|
|
952
994
|
readonly outputTokenCost: 1.8;
|
|
953
|
-
readonly thresholdTokens:
|
|
995
|
+
readonly thresholdTokens: 272000;
|
|
954
996
|
};
|
|
955
997
|
readonly reasoning: {
|
|
956
998
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -980,7 +1022,7 @@ export declare const textModels: readonly [{
|
|
|
980
1022
|
readonly inputTokenCost: 2;
|
|
981
1023
|
readonly cachedInputTokenCost: 0.2;
|
|
982
1024
|
readonly outputTokenCost: 12;
|
|
983
|
-
readonly outputTokensPerSecond:
|
|
1025
|
+
readonly outputTokensPerSecond: 124;
|
|
984
1026
|
readonly longContext: {
|
|
985
1027
|
readonly inputTokenCost: 4;
|
|
986
1028
|
readonly cachedInputTokenCost: 0.4;
|
|
@@ -1043,7 +1085,7 @@ export declare const textModels: readonly [{
|
|
|
1043
1085
|
readonly inputTokenCost: 0.75;
|
|
1044
1086
|
readonly cachedInputTokenCost: 0.075;
|
|
1045
1087
|
readonly outputTokenCost: 3.75;
|
|
1046
|
-
readonly outputTokensPerSecond:
|
|
1088
|
+
readonly outputTokensPerSecond: 329;
|
|
1047
1089
|
readonly inputAudioTokenCost: 1.5;
|
|
1048
1090
|
readonly reasoning: {
|
|
1049
1091
|
readonly levels: readonly ["low", "medium", "high"];
|
|
@@ -1455,6 +1497,7 @@ export declare const textModels: readonly [{
|
|
|
1455
1497
|
readonly cachedInputTokenCost: 0.25;
|
|
1456
1498
|
readonly cacheCreationInputTokenCost: 12.5;
|
|
1457
1499
|
readonly outputTokenCost: 50;
|
|
1500
|
+
readonly outputTokensPerSecond: 69;
|
|
1458
1501
|
readonly reasoning: {
|
|
1459
1502
|
readonly thinkingStyle: "adaptive";
|
|
1460
1503
|
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -1484,6 +1527,7 @@ export declare const textModels: readonly [{
|
|
|
1484
1527
|
readonly cachedInputTokenCost: 0.5;
|
|
1485
1528
|
readonly cacheCreationInputTokenCost: 6.25;
|
|
1486
1529
|
readonly outputTokenCost: 25;
|
|
1530
|
+
readonly outputTokensPerSecond: 59;
|
|
1487
1531
|
readonly reasoning: {
|
|
1488
1532
|
readonly thinkingStyle: "adaptive";
|
|
1489
1533
|
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -1625,7 +1669,7 @@ export declare const textModels: readonly [{
|
|
|
1625
1669
|
readonly cachedInputTokenCost: 0.2;
|
|
1626
1670
|
readonly cacheCreationInputTokenCost: 2.5;
|
|
1627
1671
|
readonly outputTokenCost: 10;
|
|
1628
|
-
readonly outputTokensPerSecond:
|
|
1672
|
+
readonly outputTokensPerSecond: 81;
|
|
1629
1673
|
readonly reasoning: {
|
|
1630
1674
|
readonly thinkingStyle: "adaptive";
|
|
1631
1675
|
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
package/dist/models.js
CHANGED
|
@@ -92,6 +92,14 @@ export const textToSpeechModels = [
|
|
|
92
92
|
},
|
|
93
93
|
// Gemini TTS is token-billed (text input + audio output). No maxInputChars:
|
|
94
94
|
// Gemini documents a 32k-token context, and characters are not a sound proxy.
|
|
95
|
+
{
|
|
96
|
+
type: "text-to-speech",
|
|
97
|
+
modelName: "gemini-3.1-flash-tts-preview",
|
|
98
|
+
provider: "google",
|
|
99
|
+
inputTokenCost: 1.0, // $/1M text-input tokens, verified 2026-09-21
|
|
100
|
+
outputAudioTokenCost: 20.0, // $/1M audio-output tokens
|
|
101
|
+
formats: ["pcm", "wav"],
|
|
102
|
+
},
|
|
95
103
|
{
|
|
96
104
|
type: "text-to-speech",
|
|
97
105
|
modelName: "gemini-2.5-flash-preview-tts",
|
|
@@ -626,7 +634,7 @@ export const textModels = [
|
|
|
626
634
|
inputTokenCost: 5,
|
|
627
635
|
cachedInputTokenCost: 0.5,
|
|
628
636
|
outputTokenCost: 22.5,
|
|
629
|
-
thresholdTokens:
|
|
637
|
+
thresholdTokens: 272000,
|
|
630
638
|
},
|
|
631
639
|
reasoning: {
|
|
632
640
|
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
@@ -719,7 +727,7 @@ export const textModels = [
|
|
|
719
727
|
longContext: {
|
|
720
728
|
inputTokenCost: 60,
|
|
721
729
|
outputTokenCost: 270,
|
|
722
|
-
thresholdTokens:
|
|
730
|
+
thresholdTokens: 272000,
|
|
723
731
|
},
|
|
724
732
|
reasoning: {
|
|
725
733
|
levels: ["medium", "high", "xhigh"],
|
|
@@ -755,7 +763,7 @@ export const textModels = [
|
|
|
755
763
|
inputTokenCost: 10,
|
|
756
764
|
cachedInputTokenCost: 1,
|
|
757
765
|
outputTokenCost: 45,
|
|
758
|
-
thresholdTokens:
|
|
766
|
+
thresholdTokens: 272000,
|
|
759
767
|
},
|
|
760
768
|
reasoning: {
|
|
761
769
|
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
@@ -788,7 +796,7 @@ export const textModels = [
|
|
|
788
796
|
longContext: {
|
|
789
797
|
inputTokenCost: 60,
|
|
790
798
|
outputTokenCost: 270,
|
|
791
|
-
thresholdTokens:
|
|
799
|
+
thresholdTokens: 272000,
|
|
792
800
|
},
|
|
793
801
|
reasoning: {
|
|
794
802
|
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
@@ -810,6 +818,42 @@ export const textModels = [
|
|
|
810
818
|
temperatureSupported: false,
|
|
811
819
|
provider: "openai-responses",
|
|
812
820
|
},
|
|
821
|
+
{
|
|
822
|
+
type: "text",
|
|
823
|
+
modelName: "gpt-6-astra",
|
|
824
|
+
description: "GPT-6 Astra is OpenAI's most capable model, built for the hardest end-to-end work: complex reasoning, coding, computer use, research, and document creation. 1M context window. Standard pricing for ≤272K input tokens; prompts above that are billed at 2x input/cache and 1.5x output for the whole request. Knowledge cutoff: April 2026.",
|
|
825
|
+
maxInputTokens: 1050000,
|
|
826
|
+
maxOutputTokens: 128000,
|
|
827
|
+
inputTokenCost: 10,
|
|
828
|
+
cachedInputTokenCost: 1,
|
|
829
|
+
outputTokenCost: 50,
|
|
830
|
+
outputTokensPerSecond: 69,
|
|
831
|
+
longContext: {
|
|
832
|
+
inputTokenCost: 20,
|
|
833
|
+
cachedInputTokenCost: 2,
|
|
834
|
+
outputTokenCost: 75,
|
|
835
|
+
thresholdTokens: 272000,
|
|
836
|
+
},
|
|
837
|
+
reasoning: {
|
|
838
|
+
levels: ["low", "medium", "high", "xhigh", "max"],
|
|
839
|
+
defaultLevel: "medium",
|
|
840
|
+
canDisable: false,
|
|
841
|
+
outputsThinking: false,
|
|
842
|
+
outputsSignatures: false,
|
|
843
|
+
},
|
|
844
|
+
modalities: {
|
|
845
|
+
input: ["text", "image", "pdf"],
|
|
846
|
+
output: ["text"],
|
|
847
|
+
},
|
|
848
|
+
knowledge: "2026-04-30",
|
|
849
|
+
releaseDate: "2026-09-04",
|
|
850
|
+
lastUpdated: "2026-09-04",
|
|
851
|
+
family: "gpt",
|
|
852
|
+
openWeights: false,
|
|
853
|
+
structuredOutput: true,
|
|
854
|
+
temperatureSupported: false,
|
|
855
|
+
provider: "openai",
|
|
856
|
+
},
|
|
813
857
|
{
|
|
814
858
|
type: "text",
|
|
815
859
|
modelName: "gpt-5.6-sol",
|
|
@@ -819,12 +863,12 @@ export const textModels = [
|
|
|
819
863
|
inputTokenCost: 4,
|
|
820
864
|
cachedInputTokenCost: 0.4,
|
|
821
865
|
outputTokenCost: 20,
|
|
822
|
-
outputTokensPerSecond:
|
|
866
|
+
outputTokensPerSecond: 77,
|
|
823
867
|
longContext: {
|
|
824
868
|
inputTokenCost: 8,
|
|
825
869
|
cachedInputTokenCost: 0.8,
|
|
826
870
|
outputTokenCost: 30,
|
|
827
|
-
thresholdTokens:
|
|
871
|
+
thresholdTokens: 272000,
|
|
828
872
|
},
|
|
829
873
|
reasoning: {
|
|
830
874
|
levels: ["none", "low", "medium", "high", "xhigh", "max"],
|
|
@@ -855,12 +899,12 @@ export const textModels = [
|
|
|
855
899
|
inputTokenCost: 2,
|
|
856
900
|
cachedInputTokenCost: 0.2,
|
|
857
901
|
outputTokenCost: 12,
|
|
858
|
-
outputTokensPerSecond:
|
|
902
|
+
outputTokensPerSecond: 106,
|
|
859
903
|
longContext: {
|
|
860
904
|
inputTokenCost: 4,
|
|
861
905
|
cachedInputTokenCost: 0.4,
|
|
862
906
|
outputTokenCost: 18,
|
|
863
|
-
thresholdTokens:
|
|
907
|
+
thresholdTokens: 272000,
|
|
864
908
|
},
|
|
865
909
|
reasoning: {
|
|
866
910
|
levels: ["none", "low", "medium", "high", "xhigh", "max"],
|
|
@@ -891,12 +935,12 @@ export const textModels = [
|
|
|
891
935
|
inputTokenCost: 0.2,
|
|
892
936
|
cachedInputTokenCost: 0.02,
|
|
893
937
|
outputTokenCost: 1.2,
|
|
894
|
-
outputTokensPerSecond:
|
|
938
|
+
outputTokensPerSecond: 165,
|
|
895
939
|
longContext: {
|
|
896
940
|
inputTokenCost: 0.4,
|
|
897
941
|
cachedInputTokenCost: 0.04,
|
|
898
942
|
outputTokenCost: 1.8,
|
|
899
|
-
thresholdTokens:
|
|
943
|
+
thresholdTokens: 272000,
|
|
900
944
|
},
|
|
901
945
|
reasoning: {
|
|
902
946
|
levels: ["none", "low", "medium", "high", "xhigh", "max"],
|
|
@@ -927,7 +971,7 @@ export const textModels = [
|
|
|
927
971
|
inputTokenCost: 2,
|
|
928
972
|
cachedInputTokenCost: 0.2,
|
|
929
973
|
outputTokenCost: 12,
|
|
930
|
-
outputTokensPerSecond:
|
|
974
|
+
outputTokensPerSecond: 124,
|
|
931
975
|
longContext: {
|
|
932
976
|
inputTokenCost: 4,
|
|
933
977
|
cachedInputTokenCost: 0.4,
|
|
@@ -992,7 +1036,7 @@ export const textModels = [
|
|
|
992
1036
|
inputTokenCost: 0.75,
|
|
993
1037
|
cachedInputTokenCost: 0.075,
|
|
994
1038
|
outputTokenCost: 3.75,
|
|
995
|
-
outputTokensPerSecond:
|
|
1039
|
+
outputTokensPerSecond: 329,
|
|
996
1040
|
inputAudioTokenCost: 1.5,
|
|
997
1041
|
reasoning: {
|
|
998
1042
|
levels: ["low", "medium", "high"],
|
|
@@ -1424,6 +1468,7 @@ export const textModels = [
|
|
|
1424
1468
|
cachedInputTokenCost: 0.25,
|
|
1425
1469
|
cacheCreationInputTokenCost: 12.5,
|
|
1426
1470
|
outputTokenCost: 50,
|
|
1471
|
+
outputTokensPerSecond: 69,
|
|
1427
1472
|
reasoning: {
|
|
1428
1473
|
thinkingStyle: "adaptive",
|
|
1429
1474
|
levels: ["low", "medium", "high", "xhigh", "max"],
|
|
@@ -1454,6 +1499,7 @@ export const textModels = [
|
|
|
1454
1499
|
cachedInputTokenCost: 0.5,
|
|
1455
1500
|
cacheCreationInputTokenCost: 6.25,
|
|
1456
1501
|
outputTokenCost: 25,
|
|
1502
|
+
outputTokensPerSecond: 59,
|
|
1457
1503
|
reasoning: {
|
|
1458
1504
|
thinkingStyle: "adaptive",
|
|
1459
1505
|
levels: ["low", "medium", "high", "xhigh", "max"],
|
|
@@ -1600,7 +1646,7 @@ export const textModels = [
|
|
|
1600
1646
|
cachedInputTokenCost: 0.2,
|
|
1601
1647
|
cacheCreationInputTokenCost: 2.5,
|
|
1602
1648
|
outputTokenCost: 10,
|
|
1603
|
-
outputTokensPerSecond:
|
|
1649
|
+
outputTokensPerSecond: 81,
|
|
1604
1650
|
reasoning: {
|
|
1605
1651
|
thinkingStyle: "adaptive",
|
|
1606
1652
|
levels: ["low", "medium", "high", "xhigh", "max"],
|
|
@@ -1939,7 +1985,7 @@ export const hostedTools = [
|
|
|
1939
1985
|
category: "code_execution",
|
|
1940
1986
|
description: "Run code in a sandboxed container.",
|
|
1941
1987
|
providerToolId: "code_execution",
|
|
1942
|
-
pricing: { unit: "per_hour", amount: 0.05, freeAllowance: "
|
|
1988
|
+
pricing: { unit: "per_hour", amount: 0.05, freeAllowance: "1,550 container-hours/month", note: "Free when used with web_search or web_fetch. 5-minute minimum execution time per container." },
|
|
1943
1989
|
},
|
|
1944
1990
|
{
|
|
1945
1991
|
name: "web_search",
|
|
@@ -2014,7 +2060,12 @@ export const hostedTools = [
|
|
|
2014
2060
|
description: "Grounding with Google Maps (Gemini 3 only).",
|
|
2015
2061
|
providerToolId: "google_maps",
|
|
2016
2062
|
models: ["gemini-3-pro-preview", "gemini-3.1-pro-preview", "gemini-3-flash-preview", "gemini-3.5-flash", "gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash", "gemini-3.1-flash-lite", "gemini-3.5-flash-lite"],
|
|
2017
|
-
pricing: {
|
|
2063
|
+
pricing: {
|
|
2064
|
+
unit: "per_call",
|
|
2065
|
+
amount: 0.014,
|
|
2066
|
+
freeAllowance: "5,000 grounded prompts/month (Gemini 3)",
|
|
2067
|
+
note: "$14 per 1,000 search queries on the Gemini 3 family.",
|
|
2068
|
+
},
|
|
2018
2069
|
},
|
|
2019
2070
|
{
|
|
2020
2071
|
name: "web_search",
|
|
@@ -11,6 +11,11 @@ export type SpeechClientConfig = {
|
|
|
11
11
|
/** Base-URL map (for OpenAI-compatible providers); read via resolveBaseUrl. */
|
|
12
12
|
baseUrl?: SmolConfig["baseUrl"];
|
|
13
13
|
voice: string;
|
|
14
|
+
/** How the speech should sound, in plain words: "Alarmed and urgent."
|
|
15
|
+
* Passed through as-is by the OpenAI-shaped providers (openai, groq,
|
|
16
|
+
* openai-compat, mlx); whether the model reads it is up to the model
|
|
17
|
+
* (OpenAI's tts-1 and tts-1-hd do not). The google provider ignores it. */
|
|
18
|
+
instructions?: string;
|
|
14
19
|
modelData?: ModelDataBlob;
|
|
15
20
|
/** Output format; provider-specific vocabulary (OpenAI: mp3/opus/aac/flac/wav/pcm). */
|
|
16
21
|
format?: string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import { Result } from "../types/result.js";
|
|
3
|
+
import type { SpeakFormat } from "../util/audioMime.js";
|
|
4
|
+
import { OpenAISpeechClient } from "./openai.js";
|
|
5
|
+
import type { SpeechResult } from "../speech.js";
|
|
6
|
+
/**
|
|
7
|
+
* Speech from an MLX server on localhost, usually `agency local serve
|
|
8
|
+
* --speech <model>`. The same request the OpenAI client makes, with the
|
|
9
|
+
* things the chat and embedding mlx clients also fix: the base URL has a
|
|
10
|
+
* default, the key is a placeholder the server ignores, and the cost is
|
|
11
|
+
* zero. It also never retries. The server runs one generation at a time,
|
|
12
|
+
* so a retry would wait behind the request that just timed out and then
|
|
13
|
+
* generate the same text again.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MlxSpeechClient extends OpenAISpeechClient {
|
|
16
|
+
protected makeClient(): OpenAI;
|
|
17
|
+
protected requiresKey(): boolean;
|
|
18
|
+
protected defaultFormat(): SpeakFormat;
|
|
19
|
+
/** Local speech is free. Set after the base class's cost pass, so model
|
|
20
|
+
* data that prices this model cannot override it (matching the chat mlx
|
|
21
|
+
* client, whose provider cost of 0 wins over registry pricing). */
|
|
22
|
+
speak(text: string): Promise<Result<SpeechResult>>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import { success } from "../types/result.js";
|
|
3
|
+
import { resolveBaseUrl } from "../util/provider.js";
|
|
4
|
+
import { OpenAISpeechClient } from "./openai.js";
|
|
5
|
+
/** How long one request may take. Equal to the SDK default; stated here so
|
|
6
|
+
* the ceiling is visible. A local model generating a long piece of text is
|
|
7
|
+
* slow, and the caller keeps its requests short. */
|
|
8
|
+
const MLX_SPEECH_TIMEOUT_MS = 600_000;
|
|
9
|
+
/**
|
|
10
|
+
* Speech from an MLX server on localhost, usually `agency local serve
|
|
11
|
+
* --speech <model>`. The same request the OpenAI client makes, with the
|
|
12
|
+
* things the chat and embedding mlx clients also fix: the base URL has a
|
|
13
|
+
* default, the key is a placeholder the server ignores, and the cost is
|
|
14
|
+
* zero. It also never retries. The server runs one generation at a time,
|
|
15
|
+
* so a retry would wait behind the request that just timed out and then
|
|
16
|
+
* generate the same text again.
|
|
17
|
+
*/
|
|
18
|
+
export class MlxSpeechClient extends OpenAISpeechClient {
|
|
19
|
+
makeClient() {
|
|
20
|
+
// resolveBaseUrl always returns a value for "mlx" (it has a default).
|
|
21
|
+
const baseURL = resolveBaseUrl("mlx", { baseUrl: this.config.baseUrl });
|
|
22
|
+
// The OpenAI SDK refuses an empty key. The local server never reads it.
|
|
23
|
+
return new OpenAI({
|
|
24
|
+
apiKey: "mlx-local",
|
|
25
|
+
baseURL,
|
|
26
|
+
maxRetries: 0,
|
|
27
|
+
timeout: MLX_SPEECH_TIMEOUT_MS,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
requiresKey() {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
defaultFormat() {
|
|
34
|
+
return "wav";
|
|
35
|
+
}
|
|
36
|
+
/** Local speech is free. Set after the base class's cost pass, so model
|
|
37
|
+
* data that prices this model cannot override it (matching the chat mlx
|
|
38
|
+
* client, whose provider cost of 0 wins over registry pricing). */
|
|
39
|
+
async speak(text) {
|
|
40
|
+
const result = await super.speak(text);
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
return success({
|
|
45
|
+
...result.value,
|
|
46
|
+
cost: { inputCost: 0, outputCost: 0, totalCost: 0, currency: "USD" },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
package/dist/speech/openai.d.ts
CHANGED
|
@@ -10,5 +10,8 @@ export declare class OpenAISpeechClient extends BaseSpeechClient {
|
|
|
10
10
|
protected defaultFormat(): SpeakFormat;
|
|
11
11
|
/** Provider-specific diagnostic when no API key is resolved. Subclasses override. */
|
|
12
12
|
protected noKeyMessage(): string;
|
|
13
|
+
/** Whether a request needs an API key at all. A local server ignores the
|
|
14
|
+
* key, and its client says so by returning false. */
|
|
15
|
+
protected requiresKey(): boolean;
|
|
13
16
|
protected _speak(text: string): Promise<Result<SpeechResult>>;
|
|
14
17
|
}
|
package/dist/speech/openai.js
CHANGED
|
@@ -15,10 +15,15 @@ export class OpenAISpeechClient extends BaseSpeechClient {
|
|
|
15
15
|
noKeyMessage() {
|
|
16
16
|
return "No OpenAI API key provided. Set apiKey.openAi or OPENAI_API_KEY.";
|
|
17
17
|
}
|
|
18
|
+
/** Whether a request needs an API key at all. A local server ignores the
|
|
19
|
+
* key, and its client says so by returning false. */
|
|
20
|
+
requiresKey() {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
18
23
|
// No try/catch here: BaseSpeechClient.speak() is the single
|
|
19
24
|
// redacting/logging exception boundary.
|
|
20
25
|
async _speak(text) {
|
|
21
|
-
if (!this.config.apiKey) {
|
|
26
|
+
if (this.requiresKey() && !this.config.apiKey) {
|
|
22
27
|
return failure(this.noKeyMessage());
|
|
23
28
|
}
|
|
24
29
|
// The shared contract carries format as a plain string; narrow to OpenAI's
|
|
@@ -40,6 +45,9 @@ export class OpenAISpeechClient extends BaseSpeechClient {
|
|
|
40
45
|
if (this.config.speed !== undefined) {
|
|
41
46
|
params.speed = this.config.speed;
|
|
42
47
|
}
|
|
48
|
+
if (this.config.instructions !== undefined && this.config.instructions !== "") {
|
|
49
|
+
params.instructions = this.config.instructions;
|
|
50
|
+
}
|
|
43
51
|
const res = await client.audio.speech.create(params, { signal: this.config.abortSignal });
|
|
44
52
|
const audio = new Uint8Array(await res.arrayBuffer());
|
|
45
53
|
const result = { audio, mimeType };
|
package/dist/speech.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ import { BaseSpeechClient, SpeechClientConfig } from "./speech/baseSpeechClient.
|
|
|
7
7
|
export type SpeakOptions = {
|
|
8
8
|
model: string;
|
|
9
9
|
voice: string;
|
|
10
|
+
/** How the speech should sound, in plain words: "Alarmed and urgent."
|
|
11
|
+
* Passed through as-is by the OpenAI-shaped providers (openai, groq,
|
|
12
|
+
* openai-compat, mlx); whether the model reads it is up to the model
|
|
13
|
+
* (OpenAI's tts-1 and tts-1-hd do not). The google provider ignores it. */
|
|
14
|
+
instructions?: string;
|
|
10
15
|
provider?: string;
|
|
11
16
|
modelData?: ModelDataBlob;
|
|
12
17
|
apiKey?: SmolConfig["apiKey"];
|
package/dist/speech.js
CHANGED
|
@@ -6,12 +6,14 @@ import { OpenAISpeechClient } from "./speech/openai.js";
|
|
|
6
6
|
import { GroqSpeechClient } from "./speech/groq.js";
|
|
7
7
|
import { GoogleSpeechClient } from "./speech/google.js";
|
|
8
8
|
import { OpenAiCompatSpeechClient } from "./speech/openaiCompat.js";
|
|
9
|
+
import { MlxSpeechClient } from "./speech/mlx.js";
|
|
9
10
|
// Checked before the user registry so a registered "openai" can't hijack the built-in.
|
|
10
11
|
const builtinClients = Object.create(null);
|
|
11
12
|
builtinClients["openai"] = OpenAISpeechClient;
|
|
12
13
|
builtinClients["groq"] = GroqSpeechClient;
|
|
13
14
|
builtinClients["google"] = GoogleSpeechClient;
|
|
14
15
|
builtinClients["openai-compat"] = OpenAiCompatSpeechClient;
|
|
16
|
+
builtinClients["mlx"] = MlxSpeechClient;
|
|
15
17
|
// Null-prototype so provider names like "toString"/"__proto__" can't collide
|
|
16
18
|
// with Object.prototype or pollute the registry.
|
|
17
19
|
const registered = Object.create(null);
|