smoltalk 0.15.1 → 0.15.3
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 +12 -6
- package/dist/classes/message/AssistantMessage.d.ts +12 -1
- package/dist/classes/message/AssistantMessage.js +9 -1
- package/dist/classes/message/index.d.ts +2 -1
- package/dist/clients/logprobs.d.ts +37 -0
- package/dist/clients/logprobs.js +52 -0
- package/dist/clients/openai.js +15 -0
- package/dist/clients/openaiResponses.js +21 -0
- package/dist/decide.d.ts +6 -3
- package/dist/decide.js +9 -6
- package/dist/models.js +9 -1
- package/dist/types.d.ts +31 -1
- package/dist/types.js +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -210,6 +210,7 @@ Two provider notes:
|
|
|
210
210
|
| `stream` | `boolean` | If `true`, returns an `AsyncGenerator<StreamChunk>` instead of a `Promise`. |
|
|
211
211
|
| `thinking` | `{ enabled, budgetTokens? }` | Enable extended thinking / thought signatures (Anthropic and Google). |
|
|
212
212
|
| `reasoningEffort` | `"low" \| "medium" \| "high"` | Provider-agnostic reasoning effort level. |
|
|
213
|
+
| `logprobs` | `{ top?: number }` | Return each generated token's log probability on `result.logprobs`, with `top` alternatives per token. OpenAI only; other providers ignore it. |
|
|
213
214
|
| `maxMessages` | `number` | If the message list exceeds this count, returns a failure instead of calling the API. |
|
|
214
215
|
| `abortSignal` | `AbortSignal` | Cancel an in-flight request. |
|
|
215
216
|
| `toolLoopDetection` | `ToolLoopDetection` | Detect and break tool-call loops. See below. |
|
|
@@ -614,12 +615,12 @@ const r = await decide(
|
|
|
614
615
|
);
|
|
615
616
|
```
|
|
616
617
|
|
|
617
|
-
|
|
618
|
-
|
|
618
|
+
For a Laya model name the registry does not know, set `provider: "typesafe"`
|
|
619
|
+
to select its decision protocol.
|
|
619
620
|
|
|
620
621
|
OpenRouter and Vercel AI Gateway also serve Jev over this protocol, so no
|
|
621
|
-
TypeSafe account is needed.
|
|
622
|
-
|
|
622
|
+
TypeSafe account is needed. To use OpenRouter, set `provider: "openrouter"`
|
|
623
|
+
and provide `OPENROUTER_API_KEY` or `config.apiKey.openRouter`:
|
|
623
624
|
|
|
624
625
|
```typescript
|
|
625
626
|
import { decide } from "smoltalk";
|
|
@@ -629,12 +630,17 @@ const r = await decide(
|
|
|
629
630
|
{ refund: { type: "noul", instructions: "Is the customer asking for money back?" } },
|
|
630
631
|
{
|
|
631
632
|
model: "jev-1.13",
|
|
632
|
-
|
|
633
|
-
|
|
633
|
+
provider: "openrouter",
|
|
634
|
+
apiKey: { openRouter: process.env.OPENROUTER_API_KEY },
|
|
634
635
|
},
|
|
635
636
|
);
|
|
636
637
|
```
|
|
637
638
|
|
|
639
|
+
OpenRouter requests go to `https://openrouter.ai/api/v1/systemone`. Set
|
|
640
|
+
`baseUrl.openRouter` to override the API base URL, including its version
|
|
641
|
+
path; `decide()` appends `/systemone`. Without an explicit provider,
|
|
642
|
+
`jev-1.13` still defaults to TypeSafe.
|
|
643
|
+
|
|
638
644
|
Vercel's base URL is `https://ai-gateway.vercel.sh/typesafe` and its model
|
|
639
645
|
name is `typesafe-ai/jev`, which needs `provider: "typesafe"`.
|
|
640
646
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BaseMessage, MessageClass } from "./BaseMessage.js";
|
|
3
|
-
import { CostEstimate, TextPart, ThinkingBlock, TokenUsage } from "../../types.js";
|
|
3
|
+
import { CostEstimate, TextPart, ThinkingBlock, TokenLogprob, TokenUsage } from "../../types.js";
|
|
4
4
|
import { ChatCompletionMessageParam } from "openai/resources";
|
|
5
5
|
import { Content } from "@google/genai";
|
|
6
6
|
import { ToolCall } from "../ToolCall.js";
|
|
@@ -25,6 +25,14 @@ export declare const AssistantMessageJSONSchema: z.ZodObject<{
|
|
|
25
25
|
text: z.ZodString;
|
|
26
26
|
signature: z.ZodString;
|
|
27
27
|
}, z.core.$strip>>>;
|
|
28
|
+
logprobs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
29
|
+
token: z.ZodString;
|
|
30
|
+
logprob: z.ZodNumber;
|
|
31
|
+
top: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
32
|
+
token: z.ZodString;
|
|
33
|
+
logprob: z.ZodNumber;
|
|
34
|
+
}, z.core.$strip>>>;
|
|
35
|
+
}, z.core.$strip>>>;
|
|
28
36
|
rawData: z.ZodOptional<z.ZodAny>;
|
|
29
37
|
usage: z.ZodOptional<z.ZodObject<{
|
|
30
38
|
inputTokens: z.ZodNumber;
|
|
@@ -54,6 +62,7 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
|
|
|
54
62
|
_refusal?: string | null;
|
|
55
63
|
_toolCalls?: ToolCall[];
|
|
56
64
|
_thinkingBlocks?: ThinkingBlock[];
|
|
65
|
+
_logprobs?: TokenLogprob[];
|
|
57
66
|
_rawData?: any;
|
|
58
67
|
_usage?: TokenUsage;
|
|
59
68
|
_cost?: CostEstimate;
|
|
@@ -63,6 +72,7 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
|
|
|
63
72
|
refusal?: string | null;
|
|
64
73
|
toolCalls?: ToolCall[];
|
|
65
74
|
thinkingBlocks?: ThinkingBlock[];
|
|
75
|
+
logprobs?: TokenLogprob[];
|
|
66
76
|
rawData?: any;
|
|
67
77
|
usage?: TokenUsage;
|
|
68
78
|
cost?: CostEstimate;
|
|
@@ -76,6 +86,7 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
|
|
|
76
86
|
get toolCalls(): ToolCall[] | undefined;
|
|
77
87
|
get rawData(): any;
|
|
78
88
|
get thinkingBlocks(): ThinkingBlock[] | undefined;
|
|
89
|
+
get logprobs(): TokenLogprob[] | undefined;
|
|
79
90
|
get usage(): TokenUsage | undefined;
|
|
80
91
|
get cost(): CostEstimate | undefined;
|
|
81
92
|
toJSON(): AssistantMessageJSON;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BaseMessage } from "./BaseMessage.js";
|
|
3
|
-
import { CostEstimateSchema, TextPartSchema, ThinkingBlockSchema, TokenUsageSchema, } from "../../types.js";
|
|
3
|
+
import { CostEstimateSchema, TextPartSchema, ThinkingBlockSchema, TokenLogprobSchema, TokenUsageSchema, } from "../../types.js";
|
|
4
4
|
import { ToolCall, ToolCallJSONSchema } from "../ToolCall.js";
|
|
5
5
|
import { getLogger } from "../../util/logger.js";
|
|
6
6
|
export const AssistantMessageJSONSchema = z.object({
|
|
@@ -11,6 +11,7 @@ export const AssistantMessageJSONSchema = z.object({
|
|
|
11
11
|
refusal: z.string().nullable().optional(),
|
|
12
12
|
toolCalls: z.array(ToolCallJSONSchema).optional(),
|
|
13
13
|
thinkingBlocks: z.array(ThinkingBlockSchema).optional(),
|
|
14
|
+
logprobs: z.array(TokenLogprobSchema).optional(),
|
|
14
15
|
rawData: z.any().optional(),
|
|
15
16
|
usage: TokenUsageSchema.optional(),
|
|
16
17
|
cost: CostEstimateSchema.optional(),
|
|
@@ -23,6 +24,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
23
24
|
_refusal;
|
|
24
25
|
_toolCalls;
|
|
25
26
|
_thinkingBlocks;
|
|
27
|
+
_logprobs;
|
|
26
28
|
_rawData;
|
|
27
29
|
_usage;
|
|
28
30
|
_cost;
|
|
@@ -34,6 +36,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
34
36
|
this._refusal = options.refusal;
|
|
35
37
|
this._toolCalls = options.toolCalls;
|
|
36
38
|
this._thinkingBlocks = options.thinkingBlocks;
|
|
39
|
+
this._logprobs = options.logprobs;
|
|
37
40
|
this._rawData = options.rawData;
|
|
38
41
|
this._usage = options.usage;
|
|
39
42
|
this._cost = options.cost;
|
|
@@ -65,6 +68,9 @@ export class AssistantMessage extends BaseMessage {
|
|
|
65
68
|
get thinkingBlocks() {
|
|
66
69
|
return this._thinkingBlocks;
|
|
67
70
|
}
|
|
71
|
+
get logprobs() {
|
|
72
|
+
return this._logprobs;
|
|
73
|
+
}
|
|
68
74
|
get usage() {
|
|
69
75
|
return this._usage;
|
|
70
76
|
}
|
|
@@ -80,6 +86,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
80
86
|
refusal: this.refusal,
|
|
81
87
|
toolCalls: this.toolCalls?.map((tc) => tc.toJSON()),
|
|
82
88
|
thinkingBlocks: this._thinkingBlocks,
|
|
89
|
+
logprobs: this._logprobs,
|
|
83
90
|
usage: this._usage,
|
|
84
91
|
cost: this._cost,
|
|
85
92
|
rawData: this._rawData,
|
|
@@ -100,6 +107,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
100
107
|
refusal: result.data.refusal,
|
|
101
108
|
toolCalls: result.data.toolCalls?.map((tc) => ToolCall.fromJSON(tc)),
|
|
102
109
|
thinkingBlocks: result.data.thinkingBlocks,
|
|
110
|
+
logprobs: result.data.logprobs,
|
|
103
111
|
rawData: result.data.rawData,
|
|
104
112
|
usage: result.data.usage,
|
|
105
113
|
cost: result.data.cost,
|
|
@@ -8,7 +8,7 @@ import type { AssistantMessageJSON } from "./AssistantMessage.js";
|
|
|
8
8
|
import type { DeveloperMessageJSON } from "./DeveloperMessage.js";
|
|
9
9
|
import type { SystemMessageJSON } from "./SystemMessage.js";
|
|
10
10
|
import type { ToolMessageJSON } from "./ToolMessage.js";
|
|
11
|
-
import { CostEstimate, TextPart, TokenUsage, UserContentInput, ImagePart, FilePart, AudioPart } from "../../types.js";
|
|
11
|
+
import { CostEstimate, TextPart, TokenLogprob, TokenUsage, UserContentInput, ImagePart, FilePart, AudioPart } from "../../types.js";
|
|
12
12
|
import type { ImageRef, BlobRef } from "../../util/blobRef.js";
|
|
13
13
|
export * from "./AssistantMessage.js";
|
|
14
14
|
export * from "./BaseMessage.js";
|
|
@@ -38,6 +38,7 @@ export declare function assistantMessage(content: string | Array<TextPart> | nul
|
|
|
38
38
|
text: string;
|
|
39
39
|
signature: string;
|
|
40
40
|
}>;
|
|
41
|
+
logprobs?: TokenLogprob[];
|
|
41
42
|
rawData?: any;
|
|
42
43
|
usage?: TokenUsage;
|
|
43
44
|
cost?: CostEstimate;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TokenAlternative, TokenLogprob } from "../types.js";
|
|
2
|
+
export type OpenAILogprob = {
|
|
3
|
+
token: string;
|
|
4
|
+
logprob: number;
|
|
5
|
+
top_logprobs?: TokenAlternative[];
|
|
6
|
+
};
|
|
7
|
+
/** OpenAI's per-token entries (chat `choices[].logprobs.content` and
|
|
8
|
+
* Responses `output_text.logprobs`) as smoltalk's shape. Undefined when
|
|
9
|
+
* there are none, so the result field stays absent. */
|
|
10
|
+
export declare function fromOpenAILogprobs(entries: OpenAILogprob[] | null | undefined): TokenLogprob[] | undefined;
|
|
11
|
+
/** How many alternatives per token a `logprobs` option asks for, or
|
|
12
|
+
* undefined for none. `top: 0` and a missing `top` both mean none. */
|
|
13
|
+
export declare function topAlternatives(option: {
|
|
14
|
+
top?: number;
|
|
15
|
+
} | undefined): number | undefined;
|
|
16
|
+
type OpenAIChatLogprobParams = {
|
|
17
|
+
logprobs?: true;
|
|
18
|
+
top_logprobs?: number;
|
|
19
|
+
};
|
|
20
|
+
/** The chat API's request parameters for a `logprobs` option: `logprobs: true`
|
|
21
|
+
* whenever the option is set, `top_logprobs` only when alternatives were
|
|
22
|
+
* asked for (the API rejects `top_logprobs` without `logprobs: true`). */
|
|
23
|
+
export declare function openAIChatLogprobParams(option: {
|
|
24
|
+
top?: number;
|
|
25
|
+
} | undefined): OpenAIChatLogprobParams;
|
|
26
|
+
type ResponsesOutputPart = {
|
|
27
|
+
type: string;
|
|
28
|
+
logprobs?: OpenAILogprob[];
|
|
29
|
+
};
|
|
30
|
+
type ResponsesOutputItem = {
|
|
31
|
+
type: string;
|
|
32
|
+
content?: ResponsesOutputPart[];
|
|
33
|
+
};
|
|
34
|
+
/** The logprobs of every `output_text` part of a Responses API `output`,
|
|
35
|
+
* in order, as smoltalk's shape. */
|
|
36
|
+
export declare function responsesOutputLogprobs(output: ResponsesOutputItem[]): TokenLogprob[] | undefined;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** One entry in smoltalk's shape. `top` is present only when there are
|
|
2
|
+
* alternatives, so an entry without them serializes without the key. */
|
|
3
|
+
function tokenLogprob(token, logprob, top) {
|
|
4
|
+
if (top.length === 0) {
|
|
5
|
+
return { token, logprob };
|
|
6
|
+
}
|
|
7
|
+
return { token, logprob, top };
|
|
8
|
+
}
|
|
9
|
+
/** OpenAI's wire alternative also carries a `bytes` field, which smoltalk
|
|
10
|
+
* drops; this copy keeps only the token and its logprob. */
|
|
11
|
+
function openAIAlternative(entry) {
|
|
12
|
+
return { token: entry.token, logprob: entry.logprob };
|
|
13
|
+
}
|
|
14
|
+
/** OpenAI's per-token entries (chat `choices[].logprobs.content` and
|
|
15
|
+
* Responses `output_text.logprobs`) as smoltalk's shape. Undefined when
|
|
16
|
+
* there are none, so the result field stays absent. */
|
|
17
|
+
export function fromOpenAILogprobs(entries) {
|
|
18
|
+
if (!entries || entries.length === 0) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return entries.map((entry) => tokenLogprob(entry.token, entry.logprob, (entry.top_logprobs ?? []).map(openAIAlternative)));
|
|
22
|
+
}
|
|
23
|
+
/** How many alternatives per token a `logprobs` option asks for, or
|
|
24
|
+
* undefined for none. `top: 0` and a missing `top` both mean none. */
|
|
25
|
+
export function topAlternatives(option) {
|
|
26
|
+
if (option?.top === undefined || option.top <= 0) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return option.top;
|
|
30
|
+
}
|
|
31
|
+
/** The chat API's request parameters for a `logprobs` option: `logprobs: true`
|
|
32
|
+
* whenever the option is set, `top_logprobs` only when alternatives were
|
|
33
|
+
* asked for (the API rejects `top_logprobs` without `logprobs: true`). */
|
|
34
|
+
export function openAIChatLogprobParams(option) {
|
|
35
|
+
if (option === undefined) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const top = topAlternatives(option);
|
|
39
|
+
if (top === undefined) {
|
|
40
|
+
return { logprobs: true };
|
|
41
|
+
}
|
|
42
|
+
return { logprobs: true, top_logprobs: top };
|
|
43
|
+
}
|
|
44
|
+
/** The logprobs of every `output_text` part of a Responses API `output`,
|
|
45
|
+
* in order, as smoltalk's shape. */
|
|
46
|
+
export function responsesOutputLogprobs(output) {
|
|
47
|
+
const textParts = output
|
|
48
|
+
.filter((item) => item.type === "message")
|
|
49
|
+
.flatMap((item) => item.content ?? [])
|
|
50
|
+
.filter((part) => part.type === "output_text");
|
|
51
|
+
return fromOpenAILogprobs(textParts.flatMap((part) => part.logprobs ?? []));
|
|
52
|
+
}
|
package/dist/clients/openai.js
CHANGED
|
@@ -10,6 +10,7 @@ import { extractHttpErrorFields } from "../util/httpError.js";
|
|
|
10
10
|
import { zodToOpenAITool } from "../util/tool.js";
|
|
11
11
|
import { responseFormatToJsonSchema } from "../util/jsonSchema.js";
|
|
12
12
|
import { normalizeOpenAIStopReason } from "../util/stopReason.js";
|
|
13
|
+
import { fromOpenAILogprobs, openAIChatLogprobParams, } from "./logprobs.js";
|
|
13
14
|
import { Model } from "../model.js";
|
|
14
15
|
export class SmolOpenAi extends BaseClient {
|
|
15
16
|
client;
|
|
@@ -138,6 +139,7 @@ export class SmolOpenAi extends BaseClient {
|
|
|
138
139
|
...(config.reasoningEffort && {
|
|
139
140
|
reasoning_effort: config.reasoningEffort,
|
|
140
141
|
}),
|
|
142
|
+
...openAIChatLogprobParams(config.logprobs),
|
|
141
143
|
...this.maxTokensParam(config),
|
|
142
144
|
...sanitizeAttributes(config.rawAttributes),
|
|
143
145
|
...this.buildRequestExtras(config),
|
|
@@ -217,6 +219,7 @@ export class SmolOpenAi extends BaseClient {
|
|
|
217
219
|
const { usage, cost } = this.calculateUsageAndCost(completion.usage, rawResponse);
|
|
218
220
|
const hostedToolResults = this.parseHostedToolResults(completion, config);
|
|
219
221
|
const rawStopReason = completion.choices[0]?.finish_reason ?? undefined;
|
|
222
|
+
const logprobs = fromOpenAILogprobs(completion.choices[0]?.logprobs?.content);
|
|
220
223
|
const result = {
|
|
221
224
|
output,
|
|
222
225
|
toolCalls,
|
|
@@ -231,6 +234,9 @@ export class SmolOpenAi extends BaseClient {
|
|
|
231
234
|
if (hostedToolResults.length > 0) {
|
|
232
235
|
result.hostedToolResults = hostedToolResults;
|
|
233
236
|
}
|
|
237
|
+
if (logprobs !== undefined) {
|
|
238
|
+
result.logprobs = logprobs;
|
|
239
|
+
}
|
|
234
240
|
return success(result);
|
|
235
241
|
}
|
|
236
242
|
async *_textStream(config) {
|
|
@@ -254,10 +260,15 @@ export class SmolOpenAi extends BaseClient {
|
|
|
254
260
|
let usage;
|
|
255
261
|
let cost;
|
|
256
262
|
let rawStopReason;
|
|
263
|
+
const logprobEntries = [];
|
|
257
264
|
for await (const chunk of completion) {
|
|
258
265
|
const chunkFinish = chunk.choices?.[0]?.finish_reason;
|
|
259
266
|
if (chunkFinish)
|
|
260
267
|
rawStopReason = chunkFinish;
|
|
268
|
+
const chunkLogprobs = chunk.choices?.[0]?.logprobs?.content;
|
|
269
|
+
if (chunkLogprobs) {
|
|
270
|
+
logprobEntries.push(...chunkLogprobs);
|
|
271
|
+
}
|
|
261
272
|
// Extract usage from the final chunk
|
|
262
273
|
if (chunk.usage) {
|
|
263
274
|
// Header-based cost (LiteLLM) is unsupported while streaming.
|
|
@@ -309,6 +320,7 @@ export class SmolOpenAi extends BaseClient {
|
|
|
309
320
|
toolCalls.push(toolCall);
|
|
310
321
|
yield { type: "tool_call", toolCall };
|
|
311
322
|
}
|
|
323
|
+
const logprobs = fromOpenAILogprobs(logprobEntries);
|
|
312
324
|
const result = {
|
|
313
325
|
output: content || null,
|
|
314
326
|
toolCalls,
|
|
@@ -320,6 +332,9 @@ export class SmolOpenAi extends BaseClient {
|
|
|
320
332
|
if (rawStopReason) {
|
|
321
333
|
result.rawStopReason = rawStopReason;
|
|
322
334
|
}
|
|
335
|
+
if (logprobs !== undefined) {
|
|
336
|
+
result.logprobs = logprobs;
|
|
337
|
+
}
|
|
323
338
|
yield { type: "done", result };
|
|
324
339
|
}
|
|
325
340
|
}
|
|
@@ -7,6 +7,7 @@ import { BaseClient } from "./baseClient.js";
|
|
|
7
7
|
import { zodToOpenAIResponsesTool } from "../util/tool.js";
|
|
8
8
|
import { responseFormatToJsonSchema } from "../util/jsonSchema.js";
|
|
9
9
|
import { normalizeOpenAIResponsesStopReason } from "../util/stopReason.js";
|
|
10
|
+
import { fromOpenAILogprobs, responsesOutputLogprobs, topAlternatives, } from "./logprobs.js";
|
|
10
11
|
import { sanitizeAttributes } from "../util/util.js";
|
|
11
12
|
import { WEB_SEARCH, webSearchResult, applyHostedToolCost } from "../util/hostedTools.js";
|
|
12
13
|
import { Model } from "../model.js";
|
|
@@ -131,6 +132,13 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
131
132
|
if (config.reasoningEffort) {
|
|
132
133
|
request.reasoning = { effort: config.reasoningEffort };
|
|
133
134
|
}
|
|
135
|
+
if (config.logprobs !== undefined) {
|
|
136
|
+
request.include = ["message.output_text.logprobs"];
|
|
137
|
+
const top = topAlternatives(config.logprobs);
|
|
138
|
+
if (top !== undefined) {
|
|
139
|
+
request.top_logprobs = top;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
134
142
|
Object.assign(request, sanitizeAttributes(config.rawAttributes));
|
|
135
143
|
return request;
|
|
136
144
|
}
|
|
@@ -196,6 +204,7 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
196
204
|
const { results: hostedToolResults, cost: finalCost } = applyHostedToolCost(parsed, cost, this.getModel(), this.config.modelData);
|
|
197
205
|
const incompleteReason = response.incomplete_details?.reason;
|
|
198
206
|
const rawStopReason = incompleteReason ?? response.status ?? undefined;
|
|
207
|
+
const logprobs = responsesOutputLogprobs(response.output);
|
|
199
208
|
const result = {
|
|
200
209
|
output,
|
|
201
210
|
toolCalls,
|
|
@@ -210,6 +219,9 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
210
219
|
if (hostedToolResults.length > 0) {
|
|
211
220
|
result.hostedToolResults = hostedToolResults;
|
|
212
221
|
}
|
|
222
|
+
if (logprobs !== undefined) {
|
|
223
|
+
result.logprobs = logprobs;
|
|
224
|
+
}
|
|
213
225
|
return success(result);
|
|
214
226
|
}
|
|
215
227
|
async *_textStream(config) {
|
|
@@ -231,6 +243,7 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
231
243
|
let usage;
|
|
232
244
|
let cost;
|
|
233
245
|
let finalResponse;
|
|
246
|
+
const logprobEntries = [];
|
|
234
247
|
for await (const event of stream) {
|
|
235
248
|
if (event.type === "response.completed" ||
|
|
236
249
|
event.type === "response.incomplete") {
|
|
@@ -242,6 +255,10 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
242
255
|
yield { type: "text", text: event.delta };
|
|
243
256
|
break;
|
|
244
257
|
}
|
|
258
|
+
case "response.output_text.done": {
|
|
259
|
+
logprobEntries.push(...(event.logprobs ?? []));
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
245
262
|
case "response.function_call_arguments.delta": {
|
|
246
263
|
const existing = functionCalls.get(event.item_id);
|
|
247
264
|
if (existing) {
|
|
@@ -306,6 +323,7 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
306
323
|
}
|
|
307
324
|
const incompleteReason = finalResponse?.incomplete_details?.reason;
|
|
308
325
|
const rawStopReason = incompleteReason ?? finalResponse?.status ?? undefined;
|
|
326
|
+
const logprobs = fromOpenAILogprobs(logprobEntries);
|
|
309
327
|
const result = {
|
|
310
328
|
output: content || null,
|
|
311
329
|
toolCalls,
|
|
@@ -317,6 +335,9 @@ export class SmolOpenAiResponses extends BaseClient {
|
|
|
317
335
|
if (rawStopReason) {
|
|
318
336
|
result.rawStopReason = rawStopReason;
|
|
319
337
|
}
|
|
338
|
+
if (logprobs !== undefined) {
|
|
339
|
+
result.logprobs = logprobs;
|
|
340
|
+
}
|
|
320
341
|
yield { type: "done", result };
|
|
321
342
|
}
|
|
322
343
|
}
|
package/dist/decide.d.ts
CHANGED
|
@@ -53,15 +53,18 @@ export type DecideConfig = {
|
|
|
53
53
|
model: string;
|
|
54
54
|
/** Required when the model is not in the registry. */
|
|
55
55
|
provider?: string;
|
|
56
|
-
/** API keys, nested by provider. Falls back to TYPESAFE_API_KEY. */
|
|
56
|
+
/** API keys, nested by provider. Falls back to TYPESAFE_API_KEY or OPENROUTER_API_KEY. */
|
|
57
57
|
apiKey?: {
|
|
58
58
|
typesafe?: string;
|
|
59
|
+
openRouter?: string;
|
|
59
60
|
[provider: string]: string | undefined;
|
|
60
61
|
};
|
|
61
|
-
/** Custom base URLs, nested by provider.
|
|
62
|
-
* then
|
|
62
|
+
/** Custom base URLs, nested by provider. TypeSafe falls back to TYPESAFE_BASE_URL,
|
|
63
|
+
* then its host. OpenRouter defaults to https://openrouter.ai/api/v1.
|
|
64
|
+
* Point `typesafe` at a Laya server to use Laya. */
|
|
63
65
|
baseUrl?: {
|
|
64
66
|
typesafe?: string;
|
|
67
|
+
openRouter?: string;
|
|
65
68
|
[provider: string]: string | undefined;
|
|
66
69
|
};
|
|
67
70
|
/** Refreshed model data to layer over the baked-in registry. */
|
package/dist/decide.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* `decide()` follows the shape of `embed()`: payload first, config last,
|
|
11
11
|
* provider and key and base URL resolved through the shared helpers. There
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* are two providers: `typesafe` and `openrouter`. A Laya server speaks the
|
|
13
|
+
* TypeSafe protocol, so it is reached by setting `baseUrl.typesafe`.
|
|
14
14
|
*/
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
import { success, failure } from "./types/result.js";
|
|
@@ -151,8 +151,8 @@ export async function decide(state, questions, config) {
|
|
|
151
151
|
catch (err) {
|
|
152
152
|
return failure(errorMessage(err));
|
|
153
153
|
}
|
|
154
|
-
if (provider !== DECISION_PROVIDER) {
|
|
155
|
-
return failure(`Provider "${provider}" does not answer decisions.
|
|
154
|
+
if (provider !== DECISION_PROVIDER && provider !== "openrouter") {
|
|
155
|
+
return failure(`Provider "${provider}" does not answer decisions. Use "${DECISION_PROVIDER}" or "openrouter"; set config.provider for a model the registry does not know.`);
|
|
156
156
|
}
|
|
157
157
|
// The registry entry for the requested name, when there is one. It sets
|
|
158
158
|
// the question cap and the price. A Laya model has no entry and no price.
|
|
@@ -164,15 +164,18 @@ export async function decide(state, questions, config) {
|
|
|
164
164
|
}
|
|
165
165
|
const apiKey = resolveApiKey(provider, config);
|
|
166
166
|
if (!apiKey) {
|
|
167
|
-
return failure(
|
|
167
|
+
return failure(provider === "openrouter"
|
|
168
|
+
? "No OpenRouter API key provided. Set config.apiKey.openRouter or the OPENROUTER_API_KEY environment variable."
|
|
169
|
+
: "No TypeSafe API key provided. Set config.apiKey.typesafe or the TYPESAFE_API_KEY environment variable.");
|
|
168
170
|
}
|
|
169
171
|
const baseUrl = resolveBaseUrl(provider, config).replace(/\/+$/, "");
|
|
172
|
+
const endpoint = provider === "openrouter" ? `${baseUrl}/systemone` : `${baseUrl}/v1/systemone`;
|
|
170
173
|
if (config.abortSignal?.aborted) {
|
|
171
174
|
return failure("Request was aborted");
|
|
172
175
|
}
|
|
173
176
|
let response;
|
|
174
177
|
try {
|
|
175
|
-
response = await fetch(
|
|
178
|
+
response = await fetch(endpoint, {
|
|
176
179
|
method: "POST",
|
|
177
180
|
headers: {
|
|
178
181
|
Authorization: `Bearer ${apiKey}`,
|
package/dist/models.js
CHANGED
|
@@ -1975,7 +1975,15 @@ export const decisionModels = [
|
|
|
1975
1975
|
type: "decision",
|
|
1976
1976
|
modelName: "jev-1.13",
|
|
1977
1977
|
provider: "typesafe",
|
|
1978
|
-
description: "Jev 1.13
|
|
1978
|
+
description: "Jev 1.13 through the TypeSafe decision protocol. Same price and question limit as jev-latest.",
|
|
1979
|
+
inputTokenCost: 0.042,
|
|
1980
|
+
maxQuestions: 64,
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
type: "decision",
|
|
1984
|
+
modelName: "jev-1.13",
|
|
1985
|
+
provider: "openrouter",
|
|
1986
|
+
description: "Jev 1.13 through OpenRouter. Answers yes/no, choice, and score questions with calibrated probabilities.",
|
|
1979
1987
|
inputTokenCost: 0.042,
|
|
1980
1988
|
maxQuestions: 64,
|
|
1981
1989
|
},
|
package/dist/types.d.ts
CHANGED
|
@@ -108,6 +108,12 @@ export type SmolConfig = {
|
|
|
108
108
|
};
|
|
109
109
|
/** Provider-agnostic reasoning effort level. */
|
|
110
110
|
reasoningEffort?: "low" | "medium" | "high";
|
|
111
|
+
/** Ask for the probability of each generated token. `top` is how many
|
|
112
|
+
* alternatives to return per token, 0 or absent for none. Honoured by
|
|
113
|
+
* OpenAI (both APIs); other providers ignore it. */
|
|
114
|
+
logprobs?: {
|
|
115
|
+
top?: number;
|
|
116
|
+
};
|
|
111
117
|
responseFormatOptions?: Partial<{
|
|
112
118
|
name: string;
|
|
113
119
|
strict: boolean;
|
|
@@ -154,6 +160,9 @@ export type PromptResult = {
|
|
|
154
160
|
output: string | null;
|
|
155
161
|
toolCalls: ToolCall[];
|
|
156
162
|
thinkingBlocks?: ThinkingBlock[];
|
|
163
|
+
/** Per-token log probabilities, when the call asked for them and the
|
|
164
|
+
* provider returned them. Absent otherwise. */
|
|
165
|
+
logprobs?: TokenLogprob[];
|
|
157
166
|
usage?: TokenUsage;
|
|
158
167
|
cost?: CostEstimate;
|
|
159
168
|
model?: ModelName;
|
|
@@ -167,7 +176,7 @@ export type PromptResult = {
|
|
|
167
176
|
* with probabilities here. */
|
|
168
177
|
rawData?: unknown;
|
|
169
178
|
};
|
|
170
|
-
export declare function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, hostedToolResults, stopReason, rawStopReason, rawData, }: Partial<PromptResult>): PromptResult;
|
|
179
|
+
export declare function promptResult({ output, toolCalls, thinkingBlocks, logprobs, usage, cost, model, hostedToolResults, stopReason, rawStopReason, rawData, }: Partial<PromptResult>): PromptResult;
|
|
171
180
|
export type StreamChunk = {
|
|
172
181
|
type: "text";
|
|
173
182
|
text: string;
|
|
@@ -216,3 +225,24 @@ export declare const ThinkingBlockSchema: z.ZodObject<{
|
|
|
216
225
|
text: z.ZodString;
|
|
217
226
|
signature: z.ZodString;
|
|
218
227
|
}, z.z.core.$strip>;
|
|
228
|
+
/** A token the model could have produced at a position, and the log of
|
|
229
|
+
* its probability. */
|
|
230
|
+
export type TokenAlternative = {
|
|
231
|
+
token: string;
|
|
232
|
+
logprob: number;
|
|
233
|
+
};
|
|
234
|
+
/** One generated token and the log of its probability. `top` holds the
|
|
235
|
+
* most likely alternatives at that position when the call asked for them. */
|
|
236
|
+
export type TokenLogprob = {
|
|
237
|
+
token: string;
|
|
238
|
+
logprob: number;
|
|
239
|
+
top?: TokenAlternative[];
|
|
240
|
+
};
|
|
241
|
+
export declare const TokenLogprobSchema: z.ZodObject<{
|
|
242
|
+
token: z.ZodString;
|
|
243
|
+
logprob: z.ZodNumber;
|
|
244
|
+
top: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
245
|
+
token: z.ZodString;
|
|
246
|
+
logprob: z.ZodNumber;
|
|
247
|
+
}, z.z.core.$strip>>>;
|
|
248
|
+
}, z.z.core.$strip>;
|
package/dist/types.js
CHANGED
|
@@ -4,11 +4,12 @@ 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, rawData, }) {
|
|
7
|
+
export function promptResult({ output, toolCalls, thinkingBlocks, logprobs, usage, cost, model, hostedToolResults, stopReason, rawStopReason, rawData, }) {
|
|
8
8
|
return {
|
|
9
9
|
output: output || null,
|
|
10
10
|
toolCalls: toolCalls || [],
|
|
11
11
|
thinkingBlocks: thinkingBlocks,
|
|
12
|
+
logprobs,
|
|
12
13
|
usage,
|
|
13
14
|
cost,
|
|
14
15
|
model,
|
|
@@ -22,3 +23,12 @@ export const ThinkingBlockSchema = z.object({
|
|
|
22
23
|
text: z.string(),
|
|
23
24
|
signature: z.string(),
|
|
24
25
|
});
|
|
26
|
+
const TokenAlternativeSchema = z.object({
|
|
27
|
+
token: z.string(),
|
|
28
|
+
logprob: z.number(),
|
|
29
|
+
});
|
|
30
|
+
export const TokenLogprobSchema = z.object({
|
|
31
|
+
token: z.string(),
|
|
32
|
+
logprob: z.number(),
|
|
33
|
+
top: z.array(TokenAlternativeSchema).optional(),
|
|
34
|
+
});
|