smoltalk 0.14.1 → 0.15.0
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 +90 -0
- package/dist/decide.d.ts +91 -0
- package/dist/decide.js +223 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/model.js +4 -3
- package/dist/models.d.ts +74 -14
- package/dist/models.js +88 -15
- package/dist/types.d.ts +5 -1
- package/dist/types.js +2 -1
- package/dist/util/provider.d.ts +2 -0
- package/dist/util/provider.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -548,6 +548,96 @@ same way and needs `smoltalk-llama-cpp` >= 0.5.0; the vector is computed in
|
|
|
548
548
|
process. See that package's README for the embedding caveats (dimension
|
|
549
549
|
truncation, one model file per role).
|
|
550
550
|
|
|
551
|
+
## Decision models (Jev, Laya)
|
|
552
|
+
|
|
553
|
+
A decision model does not write text. You send it a state and a map of typed
|
|
554
|
+
questions, and it answers every question in one pass with probabilities.
|
|
555
|
+
TypeSafe's Jev is one. Laya is an open-weights model that speaks the same
|
|
556
|
+
protocol.
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
import { decide } from "smoltalk";
|
|
560
|
+
|
|
561
|
+
const r = await decide(
|
|
562
|
+
{ subject: "Refund not received", body: "I cancelled two weeks ago..." },
|
|
563
|
+
{
|
|
564
|
+
department: {
|
|
565
|
+
type: "choice",
|
|
566
|
+
instructions: "Which team should handle this?",
|
|
567
|
+
criteria: { billing: "payments, refunds", support: "help, bugs" },
|
|
568
|
+
},
|
|
569
|
+
urgency: {
|
|
570
|
+
type: "score",
|
|
571
|
+
instructions: "How urgent?",
|
|
572
|
+
criteria: ["not urgent", "urgent", "critical"],
|
|
573
|
+
},
|
|
574
|
+
churn: { type: "noul", instructions: "Likely to cancel?" },
|
|
575
|
+
},
|
|
576
|
+
{ model: "jev-latest" },
|
|
577
|
+
);
|
|
578
|
+
if (r.success) {
|
|
579
|
+
r.value.answers.department; // { type: "choice", choice: "billing", confidence: 0.86, probabilities: {...} }
|
|
580
|
+
r.value.answers.urgency; // { type: "score", score: 1.2, confidence: 0.6, legend: {...}, probabilities: {...} }
|
|
581
|
+
r.value.answers.churn; // { type: "noul", noul: 0.1 }
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
The three question types:
|
|
586
|
+
|
|
587
|
+
- `noul` is a yes/no question. The answer is the probability of yes.
|
|
588
|
+
- `choice` picks one option from `criteria`, a map of option key to
|
|
589
|
+
description. The answer is the key, a confidence, and a probability per
|
|
590
|
+
option.
|
|
591
|
+
- `score` places the state on `criteria`, an ordered list of level
|
|
592
|
+
descriptions. The answer is an expected level, which may be fractional, a
|
|
593
|
+
confidence, a legend, and a probability per level.
|
|
594
|
+
|
|
595
|
+
The key comes from `config.apiKey.typesafe` or `TYPESAFE_API_KEY`. Cost is
|
|
596
|
+
priced from the registry entry of the model you asked for, so a model the
|
|
597
|
+
registry does not know has no cost.
|
|
598
|
+
|
|
599
|
+
To use a Laya server, run `laya-serve` and point the provider at it. Laya
|
|
600
|
+
needs no key, but the provider requires one, so pass any value:
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
import { decide } from "smoltalk";
|
|
604
|
+
|
|
605
|
+
const r = await decide(
|
|
606
|
+
"I was charged twice for my subscription.",
|
|
607
|
+
{ refund: { type: "noul", instructions: "Is the customer asking for money back?" } },
|
|
608
|
+
{
|
|
609
|
+
model: "laya",
|
|
610
|
+
provider: "typesafe",
|
|
611
|
+
apiKey: { typesafe: "unused" },
|
|
612
|
+
baseUrl: { typesafe: "http://localhost:8000" },
|
|
613
|
+
},
|
|
614
|
+
);
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
`provider: "typesafe"` is required for any model name the registry does not
|
|
618
|
+
know. It says "this endpoint speaks the decision protocol".
|
|
619
|
+
|
|
620
|
+
OpenRouter and Vercel AI Gateway also serve Jev over this protocol, so no
|
|
621
|
+
TypeSafe account is needed. OpenRouter's model name, `jev-1.13`, is in the
|
|
622
|
+
registry:
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
import { decide } from "smoltalk";
|
|
626
|
+
|
|
627
|
+
const r = await decide(
|
|
628
|
+
"I was charged twice for my subscription.",
|
|
629
|
+
{ refund: { type: "noul", instructions: "Is the customer asking for money back?" } },
|
|
630
|
+
{
|
|
631
|
+
model: "jev-1.13",
|
|
632
|
+
apiKey: { typesafe: process.env.OPENROUTER_API_KEY },
|
|
633
|
+
baseUrl: { typesafe: "https://openrouter.ai/api" },
|
|
634
|
+
},
|
|
635
|
+
);
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Vercel's base URL is `https://ai-gateway.vercel.sh/typesafe` and its model
|
|
639
|
+
name is `typesafe-ai/jev`, which needs `provider: "typesafe"`.
|
|
640
|
+
|
|
551
641
|
## Audio (STT/TTS)
|
|
552
642
|
|
|
553
643
|
Three audio primitives. `transcribe()` (speech-to-text) and `speak()`
|
package/dist/decide.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ModelDataBlob } from "./modelData.js";
|
|
2
|
+
import { Result } from "./types/result.js";
|
|
3
|
+
import { TokenUsage } from "./types/tokenUsage.js";
|
|
4
|
+
import { CostEstimate } from "./types/costEstimate.js";
|
|
5
|
+
export declare const DECISION_PROVIDER = "typesafe";
|
|
6
|
+
export type NoulQuestion = {
|
|
7
|
+
type: "noul";
|
|
8
|
+
instructions: string;
|
|
9
|
+
criteria?: {
|
|
10
|
+
true: string;
|
|
11
|
+
false: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export type ChoiceQuestion = {
|
|
15
|
+
type: "choice";
|
|
16
|
+
instructions: string;
|
|
17
|
+
/** Option key to its description. */
|
|
18
|
+
criteria: Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
export type ScoreQuestion = {
|
|
21
|
+
type: "score";
|
|
22
|
+
instructions: string;
|
|
23
|
+
/** Ordered level descriptions, lowest first. At least two. */
|
|
24
|
+
criteria: string[];
|
|
25
|
+
};
|
|
26
|
+
export type DecisionQuestion = NoulQuestion | ChoiceQuestion | ScoreQuestion;
|
|
27
|
+
export type NoulAnswer = {
|
|
28
|
+
type: "noul";
|
|
29
|
+
/** Probability that the answer is yes, 0 to 1. */
|
|
30
|
+
noul: number;
|
|
31
|
+
};
|
|
32
|
+
export type ChoiceAnswer = {
|
|
33
|
+
type: "choice";
|
|
34
|
+
choice: string;
|
|
35
|
+
confidence: number;
|
|
36
|
+
/** Option key to probability. */
|
|
37
|
+
probabilities: Record<string, number>;
|
|
38
|
+
};
|
|
39
|
+
export type ScoreAnswer = {
|
|
40
|
+
type: "score";
|
|
41
|
+
/** Expected level. May be fractional. */
|
|
42
|
+
score: number;
|
|
43
|
+
confidence: number;
|
|
44
|
+
/** Level index to description. */
|
|
45
|
+
legend: Record<string, string>;
|
|
46
|
+
/** Level index to probability. */
|
|
47
|
+
probabilities: Record<string, number>;
|
|
48
|
+
};
|
|
49
|
+
export type DecisionAnswer = NoulAnswer | ChoiceAnswer | ScoreAnswer;
|
|
50
|
+
/** What the model reads before answering: text, or a JSON value. */
|
|
51
|
+
export type DecisionState = string | object;
|
|
52
|
+
export type DecideConfig = {
|
|
53
|
+
model: string;
|
|
54
|
+
/** Required when the model is not in the registry. */
|
|
55
|
+
provider?: string;
|
|
56
|
+
/** API keys, nested by provider. Falls back to TYPESAFE_API_KEY. */
|
|
57
|
+
apiKey?: {
|
|
58
|
+
typesafe?: string;
|
|
59
|
+
[provider: string]: string | undefined;
|
|
60
|
+
};
|
|
61
|
+
/** Custom base URLs, nested by provider. Falls back to TYPESAFE_BASE_URL,
|
|
62
|
+
* then TypeSafe's host. Point `typesafe` at a Laya server to use Laya. */
|
|
63
|
+
baseUrl?: {
|
|
64
|
+
typesafe?: string;
|
|
65
|
+
[provider: string]: string | undefined;
|
|
66
|
+
};
|
|
67
|
+
/** Refreshed model data to layer over the baked-in registry. */
|
|
68
|
+
modelData?: ModelDataBlob;
|
|
69
|
+
abortSignal?: AbortSignal;
|
|
70
|
+
};
|
|
71
|
+
export type DecideResult = {
|
|
72
|
+
answers: Record<string, DecisionAnswer>;
|
|
73
|
+
usage: TokenUsage;
|
|
74
|
+
cost?: CostEstimate;
|
|
75
|
+
/** The versioned model the server reports, e.g. `jev-1.13`. */
|
|
76
|
+
model: string;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Ask a decision model one or more typed questions about a state.
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* const r = await decide(ticket, {
|
|
83
|
+
* department: { type: "choice", instructions: "Which team?", criteria: { billing: "refunds", support: "bugs" } },
|
|
84
|
+
* churn: { type: "noul", instructions: "Likely to cancel?" },
|
|
85
|
+
* }, { model: "jev-latest" });
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Cost is priced from the registry entry of the **requested** model, so a
|
|
89
|
+
* model the registry does not know, such as a Laya checkpoint, has no cost.
|
|
90
|
+
*/
|
|
91
|
+
export declare function decide(state: DecisionState, questions: Record<string, DecisionQuestion>, config: DecideConfig): Promise<Result<DecideResult>>;
|
package/dist/decide.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision models: typed questions in, probabilities out.
|
|
3
|
+
*
|
|
4
|
+
* A decision model such as TypeSafe's Jev, or the open-weights Laya, does not
|
|
5
|
+
* generate text. You send it a state (text or JSON) and a map of questions,
|
|
6
|
+
* and it answers every question in one forward pass. There are three question
|
|
7
|
+
* types: a yes/no `noul`, a `choice` from a fixed set of options, and a
|
|
8
|
+
* `score` on an ordered rubric. Every answer carries probabilities.
|
|
9
|
+
*
|
|
10
|
+
* `decide()` follows the shape of `embed()`: payload first, config last,
|
|
11
|
+
* provider and key and base URL resolved through the shared helpers. There
|
|
12
|
+
* is one provider, `typesafe`, which is the wire protocol. A Laya server
|
|
13
|
+
* speaks the same protocol, so it is reached by setting `baseUrl.typesafe`.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { success, failure } from "./types/result.js";
|
|
17
|
+
import { resolveProvider, resolveApiKey, resolveBaseUrl } from "./util/provider.js";
|
|
18
|
+
import { isDecisionModel, resolveModelForProvider } from "./models.js";
|
|
19
|
+
import { round } from "./util/util.js";
|
|
20
|
+
export const DECISION_PROVIDER = "typesafe";
|
|
21
|
+
const NoulAnswerSchema = z.object({
|
|
22
|
+
type: z.literal("noul"),
|
|
23
|
+
noul: z.number(),
|
|
24
|
+
});
|
|
25
|
+
const ChoiceAnswerSchema = z.object({
|
|
26
|
+
type: z.literal("choice"),
|
|
27
|
+
choice: z.string(),
|
|
28
|
+
confidence: z.number(),
|
|
29
|
+
probabilities: z.record(z.string(), z.number()),
|
|
30
|
+
});
|
|
31
|
+
const ScoreAnswerSchema = z.object({
|
|
32
|
+
type: z.literal("score"),
|
|
33
|
+
score: z.number(),
|
|
34
|
+
confidence: z.number(),
|
|
35
|
+
legend: z.record(z.string(), z.string()),
|
|
36
|
+
probabilities: z.record(z.string(), z.number()),
|
|
37
|
+
});
|
|
38
|
+
const DecisionAnswerSchema = z.discriminatedUnion("type", [
|
|
39
|
+
NoulAnswerSchema,
|
|
40
|
+
ChoiceAnswerSchema,
|
|
41
|
+
ScoreAnswerSchema,
|
|
42
|
+
]);
|
|
43
|
+
const ResponseSchema = z.object({
|
|
44
|
+
model: z.string().optional(),
|
|
45
|
+
answers: z.record(z.string(), DecisionAnswerSchema),
|
|
46
|
+
usage: z
|
|
47
|
+
.object({
|
|
48
|
+
input_tokens: z.number(),
|
|
49
|
+
output_tokens: z.number().optional(),
|
|
50
|
+
})
|
|
51
|
+
.optional(),
|
|
52
|
+
});
|
|
53
|
+
function errorMessage(err) {
|
|
54
|
+
if (err instanceof Error) {
|
|
55
|
+
return err.message;
|
|
56
|
+
}
|
|
57
|
+
return String(err);
|
|
58
|
+
}
|
|
59
|
+
/** A problem with the questions that no server would accept. Checked before
|
|
60
|
+
* any request so a bad question map never costs a round trip. */
|
|
61
|
+
function checkQuestions(questions, maxQuestions) {
|
|
62
|
+
const names = Object.keys(questions);
|
|
63
|
+
if (names.length === 0) {
|
|
64
|
+
return "No questions given. A decision request needs at least one question.";
|
|
65
|
+
}
|
|
66
|
+
if (maxQuestions !== undefined && names.length > maxQuestions) {
|
|
67
|
+
return `Too many questions: ${names.length} given, the model accepts at most ${maxQuestions} per request.`;
|
|
68
|
+
}
|
|
69
|
+
for (const name of names) {
|
|
70
|
+
const q = questions[name];
|
|
71
|
+
if (q.type === "choice" && Object.keys(q.criteria).length < 2) {
|
|
72
|
+
return `Question "${name}" is a choice with fewer than two options.`;
|
|
73
|
+
}
|
|
74
|
+
if (q.type === "score" && q.criteria.length < 2) {
|
|
75
|
+
return `Question "${name}" is a score with fewer than two levels.`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
function isProbability(n) {
|
|
81
|
+
return n >= 0 && n <= 1;
|
|
82
|
+
}
|
|
83
|
+
/** A response that parsed but does not answer what was asked. The Zod
|
|
84
|
+
* schema only says each field is a number or a string; this checks the
|
|
85
|
+
* numbers are probabilities and the answer fits the question it is for. */
|
|
86
|
+
function checkAnswers(questions, answers) {
|
|
87
|
+
for (const name of Object.keys(questions)) {
|
|
88
|
+
const q = questions[name];
|
|
89
|
+
const a = answers[name];
|
|
90
|
+
if (a === undefined) {
|
|
91
|
+
return `The response has no answer for question "${name}".`;
|
|
92
|
+
}
|
|
93
|
+
if (a.type !== q.type) {
|
|
94
|
+
return `Question "${name}" is a ${q.type} but the answer is a ${a.type}.`;
|
|
95
|
+
}
|
|
96
|
+
if (a.type === "noul" && !isProbability(a.noul)) {
|
|
97
|
+
return `Question "${name}" has a noul of ${a.noul}, which is not between 0 and 1.`;
|
|
98
|
+
}
|
|
99
|
+
if ((a.type === "choice" || a.type === "score") && !isProbability(a.confidence)) {
|
|
100
|
+
return `Question "${name}" has a confidence of ${a.confidence}, which is not between 0 and 1.`;
|
|
101
|
+
}
|
|
102
|
+
if (q.type === "choice" && a.type === "choice") {
|
|
103
|
+
if (!(a.choice in q.criteria)) {
|
|
104
|
+
return `Question "${name}" answered "${a.choice}", which is not one of its options.`;
|
|
105
|
+
}
|
|
106
|
+
const unknown = Object.keys(a.probabilities).find((k) => !(k in q.criteria));
|
|
107
|
+
if (unknown !== undefined) {
|
|
108
|
+
return `Question "${name}" has a probability for "${unknown}", which is not one of its options.`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (q.type === "score" && a.type === "score") {
|
|
112
|
+
const levels = q.criteria.length;
|
|
113
|
+
if (Object.keys(a.legend).length !== levels) {
|
|
114
|
+
return `Question "${name}" has ${levels} levels but the answer's legend has ${Object.keys(a.legend).length}.`;
|
|
115
|
+
}
|
|
116
|
+
if (Object.keys(a.probabilities).length !== levels) {
|
|
117
|
+
return `Question "${name}" has ${levels} levels but the answer has ${Object.keys(a.probabilities).length} probabilities.`;
|
|
118
|
+
}
|
|
119
|
+
if (a.score < 0 || a.score > levels - 1) {
|
|
120
|
+
return `Question "${name}" has a score of ${a.score}, outside its ${levels} levels.`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
function calculateDecisionCost(model, inputTokens) {
|
|
127
|
+
if (model === undefined || model.inputTokenCost === undefined) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
const inputCost = round((inputTokens * model.inputTokenCost) / 1_000_000, 6);
|
|
131
|
+
return { inputCost, outputCost: 0, totalCost: inputCost, currency: "USD" };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Ask a decision model one or more typed questions about a state.
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* const r = await decide(ticket, {
|
|
138
|
+
* department: { type: "choice", instructions: "Which team?", criteria: { billing: "refunds", support: "bugs" } },
|
|
139
|
+
* churn: { type: "noul", instructions: "Likely to cancel?" },
|
|
140
|
+
* }, { model: "jev-latest" });
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* Cost is priced from the registry entry of the **requested** model, so a
|
|
144
|
+
* model the registry does not know, such as a Laya checkpoint, has no cost.
|
|
145
|
+
*/
|
|
146
|
+
export async function decide(state, questions, config) {
|
|
147
|
+
let provider;
|
|
148
|
+
try {
|
|
149
|
+
provider = resolveProvider(config.model, config.provider, config.modelData);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
return failure(errorMessage(err));
|
|
153
|
+
}
|
|
154
|
+
if (provider !== DECISION_PROVIDER) {
|
|
155
|
+
return failure(`Provider "${provider}" does not answer decisions. Only "${DECISION_PROVIDER}" does; set config.provider to it for a model the registry does not know.`);
|
|
156
|
+
}
|
|
157
|
+
// The registry entry for the requested name, when there is one. It sets
|
|
158
|
+
// the question cap and the price. A Laya model has no entry and no price.
|
|
159
|
+
const found = resolveModelForProvider(provider, config.model, config.modelData);
|
|
160
|
+
const registryModel = found && isDecisionModel(found) ? found : undefined;
|
|
161
|
+
const questionProblem = checkQuestions(questions, registryModel?.maxQuestions);
|
|
162
|
+
if (questionProblem) {
|
|
163
|
+
return failure(questionProblem);
|
|
164
|
+
}
|
|
165
|
+
const apiKey = resolveApiKey(provider, config);
|
|
166
|
+
if (!apiKey) {
|
|
167
|
+
return failure("No TypeSafe API key provided. Set config.apiKey.typesafe or the TYPESAFE_API_KEY environment variable.");
|
|
168
|
+
}
|
|
169
|
+
const baseUrl = resolveBaseUrl(provider, config).replace(/\/+$/, "");
|
|
170
|
+
if (config.abortSignal?.aborted) {
|
|
171
|
+
return failure("Request was aborted");
|
|
172
|
+
}
|
|
173
|
+
let response;
|
|
174
|
+
try {
|
|
175
|
+
response = await fetch(`${baseUrl}/v1/systemone`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: {
|
|
178
|
+
Authorization: `Bearer ${apiKey}`,
|
|
179
|
+
"Content-Type": "application/json",
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify({ model: config.model, state, questions }),
|
|
182
|
+
signal: config.abortSignal,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (config.abortSignal?.aborted) {
|
|
187
|
+
return failure("Request was aborted");
|
|
188
|
+
}
|
|
189
|
+
return failure(`Decision request failed: ${errorMessage(err)}`);
|
|
190
|
+
}
|
|
191
|
+
const text = await response.text();
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
return failure(`Decision request failed with status ${response.status}: ${text}`);
|
|
194
|
+
}
|
|
195
|
+
let body;
|
|
196
|
+
try {
|
|
197
|
+
body = JSON.parse(text);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return failure(`Decision response is not JSON: ${text.slice(0, 200)}`);
|
|
201
|
+
}
|
|
202
|
+
const parsed = ResponseSchema.safeParse(body);
|
|
203
|
+
if (!parsed.success) {
|
|
204
|
+
const issue = parsed.error.issues[0];
|
|
205
|
+
const where = issue.path.length > 0 ? ` at ${issue.path.join(".")}` : "";
|
|
206
|
+
return failure(`Decision response has an unexpected shape${where}: ${issue.message}`);
|
|
207
|
+
}
|
|
208
|
+
const answers = parsed.data.answers;
|
|
209
|
+
const answerProblem = checkAnswers(questions, answers);
|
|
210
|
+
if (answerProblem) {
|
|
211
|
+
return failure(answerProblem);
|
|
212
|
+
}
|
|
213
|
+
// Output is free, so only input tokens are priced. The server's output
|
|
214
|
+
// count is still reported: Jev sends 0, but a gateway may count its own.
|
|
215
|
+
const inputTokens = parsed.data.usage?.input_tokens ?? 0;
|
|
216
|
+
const outputTokens = parsed.data.usage?.output_tokens ?? 0;
|
|
217
|
+
return success({
|
|
218
|
+
answers,
|
|
219
|
+
usage: { inputTokens, outputTokens },
|
|
220
|
+
cost: calculateDecisionCost(registryModel, inputTokens),
|
|
221
|
+
model: parsed.data.model ?? config.model,
|
|
222
|
+
});
|
|
223
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { loadLlamaCpp } from "./clients/llamaCppLoader.js";
|
|
|
13
13
|
export type { LlamaCppModule } from "./clients/llamaCppLoader.js";
|
|
14
14
|
export * from "./classes/ToolCall.js";
|
|
15
15
|
export * from "./embed.js";
|
|
16
|
+
export * from "./decide.js";
|
|
17
|
+
export { resolveProvider } from "./util/provider.js";
|
|
16
18
|
export * from "./image.js";
|
|
17
19
|
export { uploadFile, deleteFile, registerFileProvider, DEFAULT_UPLOAD_BYTES } from "./files.js";
|
|
18
20
|
export type { UploadFileOptions, FileProviderContext, FileProvider } from "./files.js";
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ export * from "./functions.js";
|
|
|
13
13
|
export { loadLlamaCpp } from "./clients/llamaCppLoader.js";
|
|
14
14
|
export * from "./classes/ToolCall.js";
|
|
15
15
|
export * from "./embed.js";
|
|
16
|
+
export * from "./decide.js";
|
|
17
|
+
export { resolveProvider } from "./util/provider.js";
|
|
16
18
|
export * from "./image.js";
|
|
17
19
|
// Explicit (not `export *`) so the test-only `_resetForTests` stays off the public surface.
|
|
18
20
|
export { uploadFile, deleteFile, registerFileProvider, DEFAULT_UPLOAD_BYTES } from "./files.js";
|
package/dist/model.js
CHANGED
|
@@ -36,9 +36,10 @@ export class Model {
|
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
// This token engine prices text generation and token-billed audio models
|
|
39
|
-
// (e.g. Gemini TTS). Image and
|
|
40
|
-
// so they are never priced here even if they carry text-token
|
|
41
|
-
|
|
39
|
+
// (e.g. Gemini TTS). Image, embeddings, and decision models have their own
|
|
40
|
+
// cost paths, so they are never priced here even if they carry text-token
|
|
41
|
+
// rates.
|
|
42
|
+
if (model.type === "image" || model.type === "embeddings" || model.type === "decision") {
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
44
45
|
// BaseModel token-rate fields, read structurally across the model union.
|
package/dist/models.d.ts
CHANGED
|
@@ -115,7 +115,21 @@ export type EmbeddingsModel = {
|
|
|
115
115
|
provider: string;
|
|
116
116
|
tokenCost?: number;
|
|
117
117
|
};
|
|
118
|
-
|
|
118
|
+
/**
|
|
119
|
+
* A decision model answers typed questions about a state with probabilities
|
|
120
|
+
* instead of generating text. See `lib/decide.ts`.
|
|
121
|
+
*/
|
|
122
|
+
export type DecisionModel = {
|
|
123
|
+
type: "decision";
|
|
124
|
+
modelName: string;
|
|
125
|
+
provider: string;
|
|
126
|
+
description?: string;
|
|
127
|
+
/** Cost per 1M input tokens, in dollars. Output is free. */
|
|
128
|
+
inputTokenCost?: number;
|
|
129
|
+
/** The most questions one request may carry. */
|
|
130
|
+
maxQuestions?: number;
|
|
131
|
+
};
|
|
132
|
+
export type ModelType = SpeechToTextModel | TextToSpeechModel | TextModel | EmbeddingsModel | ImageModel | DecisionModel;
|
|
119
133
|
export declare const speechToTextModels: readonly [{
|
|
120
134
|
readonly type: "speech-to-text";
|
|
121
135
|
readonly modelName: "whisper-1";
|
|
@@ -176,6 +190,13 @@ export declare const textToSpeechModels: readonly [{
|
|
|
176
190
|
readonly perCharacterCost: 0.00004;
|
|
177
191
|
readonly maxInputChars: 200;
|
|
178
192
|
readonly formats: readonly ["wav"];
|
|
193
|
+
}, {
|
|
194
|
+
readonly type: "text-to-speech";
|
|
195
|
+
readonly modelName: "gemini-3.1-flash-tts-preview";
|
|
196
|
+
readonly provider: "google";
|
|
197
|
+
readonly inputTokenCost: 1;
|
|
198
|
+
readonly outputAudioTokenCost: 20;
|
|
199
|
+
readonly formats: readonly ["pcm", "wav"];
|
|
179
200
|
}, {
|
|
180
201
|
readonly type: "text-to-speech";
|
|
181
202
|
readonly modelName: "gemini-2.5-flash-preview-tts";
|
|
@@ -688,7 +709,7 @@ export declare const textModels: readonly [{
|
|
|
688
709
|
readonly inputTokenCost: 5;
|
|
689
710
|
readonly cachedInputTokenCost: 0.5;
|
|
690
711
|
readonly outputTokenCost: 22.5;
|
|
691
|
-
readonly thresholdTokens:
|
|
712
|
+
readonly thresholdTokens: 272000;
|
|
692
713
|
};
|
|
693
714
|
readonly reasoning: {
|
|
694
715
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh"];
|
|
@@ -778,7 +799,7 @@ export declare const textModels: readonly [{
|
|
|
778
799
|
readonly longContext: {
|
|
779
800
|
readonly inputTokenCost: 60;
|
|
780
801
|
readonly outputTokenCost: 270;
|
|
781
|
-
readonly thresholdTokens:
|
|
802
|
+
readonly thresholdTokens: 272000;
|
|
782
803
|
};
|
|
783
804
|
readonly reasoning: {
|
|
784
805
|
readonly levels: readonly ["medium", "high", "xhigh"];
|
|
@@ -813,7 +834,7 @@ export declare const textModels: readonly [{
|
|
|
813
834
|
readonly inputTokenCost: 10;
|
|
814
835
|
readonly cachedInputTokenCost: 1;
|
|
815
836
|
readonly outputTokenCost: 45;
|
|
816
|
-
readonly thresholdTokens:
|
|
837
|
+
readonly thresholdTokens: 272000;
|
|
817
838
|
};
|
|
818
839
|
readonly reasoning: {
|
|
819
840
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh"];
|
|
@@ -845,7 +866,7 @@ export declare const textModels: readonly [{
|
|
|
845
866
|
readonly longContext: {
|
|
846
867
|
readonly inputTokenCost: 60;
|
|
847
868
|
readonly outputTokenCost: 270;
|
|
848
|
-
readonly thresholdTokens:
|
|
869
|
+
readonly thresholdTokens: 272000;
|
|
849
870
|
};
|
|
850
871
|
readonly reasoning: {
|
|
851
872
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh"];
|
|
@@ -866,6 +887,41 @@ export declare const textModels: readonly [{
|
|
|
866
887
|
readonly structuredOutput: true;
|
|
867
888
|
readonly temperatureSupported: false;
|
|
868
889
|
readonly provider: "openai-responses";
|
|
890
|
+
}, {
|
|
891
|
+
readonly type: "text";
|
|
892
|
+
readonly modelName: "gpt-6-astra";
|
|
893
|
+
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.";
|
|
894
|
+
readonly maxInputTokens: 1050000;
|
|
895
|
+
readonly maxOutputTokens: 128000;
|
|
896
|
+
readonly inputTokenCost: 10;
|
|
897
|
+
readonly cachedInputTokenCost: 1;
|
|
898
|
+
readonly outputTokenCost: 50;
|
|
899
|
+
readonly outputTokensPerSecond: 69;
|
|
900
|
+
readonly longContext: {
|
|
901
|
+
readonly inputTokenCost: 20;
|
|
902
|
+
readonly cachedInputTokenCost: 2;
|
|
903
|
+
readonly outputTokenCost: 75;
|
|
904
|
+
readonly thresholdTokens: 272000;
|
|
905
|
+
};
|
|
906
|
+
readonly reasoning: {
|
|
907
|
+
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
908
|
+
readonly defaultLevel: "medium";
|
|
909
|
+
readonly canDisable: false;
|
|
910
|
+
readonly outputsThinking: false;
|
|
911
|
+
readonly outputsSignatures: false;
|
|
912
|
+
};
|
|
913
|
+
readonly modalities: {
|
|
914
|
+
readonly input: readonly ["text", "image", "pdf"];
|
|
915
|
+
readonly output: readonly ["text"];
|
|
916
|
+
};
|
|
917
|
+
readonly knowledge: "2026-04-30";
|
|
918
|
+
readonly releaseDate: "2026-09-04";
|
|
919
|
+
readonly lastUpdated: "2026-09-04";
|
|
920
|
+
readonly family: "gpt";
|
|
921
|
+
readonly openWeights: false;
|
|
922
|
+
readonly structuredOutput: true;
|
|
923
|
+
readonly temperatureSupported: false;
|
|
924
|
+
readonly provider: "openai";
|
|
869
925
|
}, {
|
|
870
926
|
readonly type: "text";
|
|
871
927
|
readonly modelName: "gpt-5.6-sol";
|
|
@@ -875,12 +931,12 @@ export declare const textModels: readonly [{
|
|
|
875
931
|
readonly inputTokenCost: 4;
|
|
876
932
|
readonly cachedInputTokenCost: 0.4;
|
|
877
933
|
readonly outputTokenCost: 20;
|
|
878
|
-
readonly outputTokensPerSecond:
|
|
934
|
+
readonly outputTokensPerSecond: 77;
|
|
879
935
|
readonly longContext: {
|
|
880
936
|
readonly inputTokenCost: 8;
|
|
881
937
|
readonly cachedInputTokenCost: 0.8;
|
|
882
938
|
readonly outputTokenCost: 30;
|
|
883
|
-
readonly thresholdTokens:
|
|
939
|
+
readonly thresholdTokens: 272000;
|
|
884
940
|
};
|
|
885
941
|
readonly reasoning: {
|
|
886
942
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -910,12 +966,12 @@ export declare const textModels: readonly [{
|
|
|
910
966
|
readonly inputTokenCost: 2;
|
|
911
967
|
readonly cachedInputTokenCost: 0.2;
|
|
912
968
|
readonly outputTokenCost: 12;
|
|
913
|
-
readonly outputTokensPerSecond:
|
|
969
|
+
readonly outputTokensPerSecond: 106;
|
|
914
970
|
readonly longContext: {
|
|
915
971
|
readonly inputTokenCost: 4;
|
|
916
972
|
readonly cachedInputTokenCost: 0.4;
|
|
917
973
|
readonly outputTokenCost: 18;
|
|
918
|
-
readonly thresholdTokens:
|
|
974
|
+
readonly thresholdTokens: 272000;
|
|
919
975
|
};
|
|
920
976
|
readonly reasoning: {
|
|
921
977
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -945,12 +1001,12 @@ export declare const textModels: readonly [{
|
|
|
945
1001
|
readonly inputTokenCost: 0.2;
|
|
946
1002
|
readonly cachedInputTokenCost: 0.02;
|
|
947
1003
|
readonly outputTokenCost: 1.2;
|
|
948
|
-
readonly outputTokensPerSecond:
|
|
1004
|
+
readonly outputTokensPerSecond: 165;
|
|
949
1005
|
readonly longContext: {
|
|
950
1006
|
readonly inputTokenCost: 0.4;
|
|
951
1007
|
readonly cachedInputTokenCost: 0.04;
|
|
952
1008
|
readonly outputTokenCost: 1.8;
|
|
953
|
-
readonly thresholdTokens:
|
|
1009
|
+
readonly thresholdTokens: 272000;
|
|
954
1010
|
};
|
|
955
1011
|
readonly reasoning: {
|
|
956
1012
|
readonly levels: readonly ["none", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -980,7 +1036,7 @@ export declare const textModels: readonly [{
|
|
|
980
1036
|
readonly inputTokenCost: 2;
|
|
981
1037
|
readonly cachedInputTokenCost: 0.2;
|
|
982
1038
|
readonly outputTokenCost: 12;
|
|
983
|
-
readonly outputTokensPerSecond:
|
|
1039
|
+
readonly outputTokensPerSecond: 124;
|
|
984
1040
|
readonly longContext: {
|
|
985
1041
|
readonly inputTokenCost: 4;
|
|
986
1042
|
readonly cachedInputTokenCost: 0.4;
|
|
@@ -1043,7 +1099,7 @@ export declare const textModels: readonly [{
|
|
|
1043
1099
|
readonly inputTokenCost: 0.75;
|
|
1044
1100
|
readonly cachedInputTokenCost: 0.075;
|
|
1045
1101
|
readonly outputTokenCost: 3.75;
|
|
1046
|
-
readonly outputTokensPerSecond:
|
|
1102
|
+
readonly outputTokensPerSecond: 329;
|
|
1047
1103
|
readonly inputAudioTokenCost: 1.5;
|
|
1048
1104
|
readonly reasoning: {
|
|
1049
1105
|
readonly levels: readonly ["low", "medium", "high"];
|
|
@@ -1455,6 +1511,7 @@ export declare const textModels: readonly [{
|
|
|
1455
1511
|
readonly cachedInputTokenCost: 0.25;
|
|
1456
1512
|
readonly cacheCreationInputTokenCost: 12.5;
|
|
1457
1513
|
readonly outputTokenCost: 50;
|
|
1514
|
+
readonly outputTokensPerSecond: 69;
|
|
1458
1515
|
readonly reasoning: {
|
|
1459
1516
|
readonly thinkingStyle: "adaptive";
|
|
1460
1517
|
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -1484,6 +1541,7 @@ export declare const textModels: readonly [{
|
|
|
1484
1541
|
readonly cachedInputTokenCost: 0.5;
|
|
1485
1542
|
readonly cacheCreationInputTokenCost: 6.25;
|
|
1486
1543
|
readonly outputTokenCost: 25;
|
|
1544
|
+
readonly outputTokensPerSecond: 59;
|
|
1487
1545
|
readonly reasoning: {
|
|
1488
1546
|
readonly thinkingStyle: "adaptive";
|
|
1489
1547
|
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -1625,7 +1683,7 @@ export declare const textModels: readonly [{
|
|
|
1625
1683
|
readonly cachedInputTokenCost: 0.2;
|
|
1626
1684
|
readonly cacheCreationInputTokenCost: 2.5;
|
|
1627
1685
|
readonly outputTokenCost: 10;
|
|
1628
|
-
readonly outputTokensPerSecond:
|
|
1686
|
+
readonly outputTokensPerSecond: 81;
|
|
1629
1687
|
readonly reasoning: {
|
|
1630
1688
|
readonly thinkingStyle: "adaptive";
|
|
1631
1689
|
readonly levels: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -1899,6 +1957,7 @@ export declare const imageModels: readonly [{
|
|
|
1899
1957
|
readonly costPerImage: 0.034;
|
|
1900
1958
|
}];
|
|
1901
1959
|
export declare const embeddingsModels: EmbeddingsModel[];
|
|
1960
|
+
export declare const decisionModels: DecisionModel[];
|
|
1902
1961
|
export type TextModelName = (typeof textModels)[number]["modelName"];
|
|
1903
1962
|
export type ImageModelName = (typeof imageModels)[number]["modelName"];
|
|
1904
1963
|
export type SpeechToTextModelName = (typeof speechToTextModels)[number]["modelName"];
|
|
@@ -1962,4 +2021,5 @@ export declare function audioInputConstraints(model: ModelType): {
|
|
|
1962
2021
|
supportedMimeTypes?: readonly string[];
|
|
1963
2022
|
};
|
|
1964
2023
|
export declare function isEmbeddingsModel(model: ModelType): model is EmbeddingsModel;
|
|
2024
|
+
export declare function isDecisionModel(model: ModelType): model is DecisionModel;
|
|
1965
2025
|
export declare const ModelNameSchema: z.ZodString;
|
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"],
|
|
@@ -1916,6 +1962,24 @@ export const embeddingsModels = [
|
|
|
1916
1962
|
tokenCost: 0.2,
|
|
1917
1963
|
},
|
|
1918
1964
|
];
|
|
1965
|
+
export const decisionModels = [
|
|
1966
|
+
{
|
|
1967
|
+
type: "decision",
|
|
1968
|
+
modelName: "jev-latest",
|
|
1969
|
+
provider: "typesafe",
|
|
1970
|
+
description: "TypeSafe's System One model. Answers yes/no, choice, and score questions about a state with calibrated probabilities. Does not generate text.",
|
|
1971
|
+
inputTokenCost: 0.042,
|
|
1972
|
+
maxQuestions: 64,
|
|
1973
|
+
},
|
|
1974
|
+
{
|
|
1975
|
+
type: "decision",
|
|
1976
|
+
modelName: "jev-1.13",
|
|
1977
|
+
provider: "typesafe",
|
|
1978
|
+
description: "Jev 1.13, the name OpenRouter serves it under (baseUrl.typesafe = https://openrouter.ai/api). Same price and protocol as jev-latest.",
|
|
1979
|
+
inputTokenCost: 0.042,
|
|
1980
|
+
maxQuestions: 64,
|
|
1981
|
+
},
|
|
1982
|
+
];
|
|
1919
1983
|
export const hostedTools = [
|
|
1920
1984
|
{
|
|
1921
1985
|
name: "web_search",
|
|
@@ -1939,7 +2003,7 @@ export const hostedTools = [
|
|
|
1939
2003
|
category: "code_execution",
|
|
1940
2004
|
description: "Run code in a sandboxed container.",
|
|
1941
2005
|
providerToolId: "code_execution",
|
|
1942
|
-
pricing: { unit: "per_hour", amount: 0.05, freeAllowance: "
|
|
2006
|
+
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
2007
|
},
|
|
1944
2008
|
{
|
|
1945
2009
|
name: "web_search",
|
|
@@ -2014,7 +2078,12 @@ export const hostedTools = [
|
|
|
2014
2078
|
description: "Grounding with Google Maps (Gemini 3 only).",
|
|
2015
2079
|
providerToolId: "google_maps",
|
|
2016
2080
|
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: {
|
|
2081
|
+
pricing: {
|
|
2082
|
+
unit: "per_call",
|
|
2083
|
+
amount: 0.014,
|
|
2084
|
+
freeAllowance: "5,000 grounded prompts/month (Gemini 3)",
|
|
2085
|
+
note: "$14 per 1,000 search queries on the Gemini 3 family.",
|
|
2086
|
+
},
|
|
2018
2087
|
},
|
|
2019
2088
|
{
|
|
2020
2089
|
name: "web_search",
|
|
@@ -2051,6 +2120,7 @@ function baselineModels() {
|
|
|
2051
2120
|
...textToSpeechModels,
|
|
2052
2121
|
...registeredTextModels,
|
|
2053
2122
|
...embeddingsModels,
|
|
2123
|
+
...decisionModels,
|
|
2054
2124
|
];
|
|
2055
2125
|
}
|
|
2056
2126
|
/**
|
|
@@ -2210,6 +2280,9 @@ export function audioInputConstraints(model) {
|
|
|
2210
2280
|
export function isEmbeddingsModel(model) {
|
|
2211
2281
|
return model.type === "embeddings";
|
|
2212
2282
|
}
|
|
2283
|
+
export function isDecisionModel(model) {
|
|
2284
|
+
return model.type === "decision";
|
|
2285
|
+
}
|
|
2213
2286
|
export const ModelNameSchema = z
|
|
2214
2287
|
.string()
|
|
2215
2288
|
.regex(/^[a-zA-Z0-9._:@/-]+$/, "Model name must only contain letters, numbers, dots, underscores, hyphens, colons, slashes, and @");
|
package/dist/types.d.ts
CHANGED
|
@@ -162,8 +162,12 @@ export type PromptResult = {
|
|
|
162
162
|
stopReason?: StopReason;
|
|
163
163
|
/** The untouched provider finish/stop-reason value (e.g. `end_turn`, `MAX_TOKENS`). */
|
|
164
164
|
rawStopReason?: string;
|
|
165
|
+
/** Whatever the provider returned beyond the text, for callers that want
|
|
166
|
+
* it on the assistant message. A decision model puts its full answers
|
|
167
|
+
* with probabilities here. */
|
|
168
|
+
rawData?: unknown;
|
|
165
169
|
};
|
|
166
|
-
export declare function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, hostedToolResults, stopReason, rawStopReason, }: Partial<PromptResult>): PromptResult;
|
|
170
|
+
export declare function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, hostedToolResults, stopReason, rawStopReason, rawData, }: Partial<PromptResult>): PromptResult;
|
|
167
171
|
export type StreamChunk = {
|
|
168
172
|
type: "text";
|
|
169
173
|
text: string;
|
package/dist/types.js
CHANGED
|
@@ -4,7 +4,7 @@ import z from "zod";
|
|
|
4
4
|
export * from "./types/costEstimate.js";
|
|
5
5
|
export * from "./types/tokenUsage.js";
|
|
6
6
|
export * from "./types/stopReason.js";
|
|
7
|
-
export function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, hostedToolResults, stopReason, rawStopReason, }) {
|
|
7
|
+
export function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, hostedToolResults, stopReason, rawStopReason, rawData, }) {
|
|
8
8
|
return {
|
|
9
9
|
output: output || null,
|
|
10
10
|
toolCalls: toolCalls || [],
|
|
@@ -15,6 +15,7 @@ export function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, m
|
|
|
15
15
|
hostedToolResults,
|
|
16
16
|
stopReason,
|
|
17
17
|
rawStopReason,
|
|
18
|
+
rawData,
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
export const ThinkingBlockSchema = z.object({
|
package/dist/util/provider.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ type NestedKeyConfig = {
|
|
|
20
20
|
liteLlm?: string;
|
|
21
21
|
openAiCompat?: string;
|
|
22
22
|
groq?: string;
|
|
23
|
+
typesafe?: string;
|
|
23
24
|
/** Arbitrary provider names, for keys targeting a custom-registered provider. */
|
|
24
25
|
[provider: string]: string | undefined;
|
|
25
26
|
};
|
|
@@ -30,6 +31,7 @@ type NestedKeyConfig = {
|
|
|
30
31
|
liteLlm?: string;
|
|
31
32
|
openAiCompat?: string;
|
|
32
33
|
mlx?: string;
|
|
34
|
+
typesafe?: string;
|
|
33
35
|
};
|
|
34
36
|
};
|
|
35
37
|
/**
|
package/dist/util/provider.js
CHANGED
|
@@ -39,6 +39,8 @@ export function resolveApiKey(provider, config) {
|
|
|
39
39
|
return k?.openAiCompat || process.env.OPENAI_COMPAT_API_KEY;
|
|
40
40
|
case "groq":
|
|
41
41
|
return k?.groq || process.env.GROQ_API_KEY;
|
|
42
|
+
case "typesafe":
|
|
43
|
+
return k?.typesafe || process.env.TYPESAFE_API_KEY;
|
|
42
44
|
default:
|
|
43
45
|
return config.apiKey?.[provider];
|
|
44
46
|
}
|
|
@@ -66,6 +68,9 @@ export function resolveBaseUrl(provider, config) {
|
|
|
66
68
|
return b?.openAiCompat || process.env.OPENAI_COMPAT_BASE_URL;
|
|
67
69
|
case "mlx":
|
|
68
70
|
return b?.mlx || process.env.MLX_BASE_URL || "http://127.0.0.1:8080/v1";
|
|
71
|
+
case "typesafe":
|
|
72
|
+
// A Laya server (`laya-serve`) speaks the same protocol; point this at it.
|
|
73
|
+
return b?.typesafe || process.env.TYPESAFE_BASE_URL || "https://api.typesafe.ai";
|
|
69
74
|
default:
|
|
70
75
|
return undefined;
|
|
71
76
|
}
|