@codefionn/llmleaf-client 0.1.2 → 0.1.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 +38 -111
- package/dist/gen/llmleaf/v1/llmleaf_pb.d.ts +117 -0
- package/dist/gen/llmleaf/v1/llmleaf_pb.d.ts.map +1 -1
- package/dist/gen/llmleaf/v1/llmleaf_pb.js +55 -45
- package/dist/gen/llmleaf/v1/llmleaf_pb.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +70 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -1
- package/dist/types.js.map +1 -1
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +83 -0
- package/dist/wire.js.map +1 -1
- package/package.json +2 -1
- package/src/gen/llmleaf/v1/llmleaf_pb.ts +181 -45
- package/src/index.ts +5 -0
- package/src/types.ts +74 -0
- package/src/wire.ts +73 -0
package/src/types.ts
CHANGED
|
@@ -25,6 +25,34 @@ export interface Usage {
|
|
|
25
25
|
totalTokens: number;
|
|
26
26
|
/** llmleaf addition; absent when the model has no known price. */
|
|
27
27
|
costUsd?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Prompt-cache hit accounting (OpenAI `usage.prompt_tokens_details`). Absent when the
|
|
30
|
+
* upstream reported no caching; {@link cachedTokens} flattens it to a plain count.
|
|
31
|
+
*/
|
|
32
|
+
promptTokensDetails?: PromptTokensDetails;
|
|
33
|
+
/**
|
|
34
|
+
* Input tokens written to the provider's prompt cache this request — a cache *write*
|
|
35
|
+
* (creation). An llmleaf extension (Anthropic reports it; OpenAI/OpenRouter do not);
|
|
36
|
+
* absent when there were none.
|
|
37
|
+
*/
|
|
38
|
+
cacheCreationTokens?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Breakdown of {@link Usage.promptTokens}. Today only the cache-read (hit) share is
|
|
43
|
+
* surfaced — the count of prompt tokens served from the provider's cache rather than
|
|
44
|
+
* processed fresh.
|
|
45
|
+
*/
|
|
46
|
+
export interface PromptTokensDetails {
|
|
47
|
+
cachedTokens?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Prompt tokens served from the provider's cache this request — a cache *read* (hit).
|
|
52
|
+
* `0` when the upstream reported no caching.
|
|
53
|
+
*/
|
|
54
|
+
export function cachedTokens(usage: Usage): number {
|
|
55
|
+
return usage.promptTokensDetails?.cachedTokens ?? 0;
|
|
28
56
|
}
|
|
29
57
|
|
|
30
58
|
// ---------------------------------------------------------------------------
|
|
@@ -63,6 +91,37 @@ export interface ToolCall {
|
|
|
63
91
|
function: FunctionCall;
|
|
64
92
|
}
|
|
65
93
|
|
|
94
|
+
/**
|
|
95
|
+
* One structured reasoning ("thinking") block (OpenRouter `reasoning_details[]`). It
|
|
96
|
+
* expresses both *open* reasoning — visible text, optionally signed — and *hidden*
|
|
97
|
+
* reasoning — an encrypted/redacted blob the provider returns in place of the text.
|
|
98
|
+
* `type` is the wire discriminator and selects which field is set:
|
|
99
|
+
*
|
|
100
|
+
* - `"reasoning.text"` → {@link text} (+ optional {@link signature}) — **open** (visible)
|
|
101
|
+
* - `"reasoning.summary"` → {@link summary} — **open** (a summarised view)
|
|
102
|
+
* - `"reasoning.encrypted"` → {@link data} — **hidden** (redacted / opaque)
|
|
103
|
+
*
|
|
104
|
+
* {@link signature} and {@link data} are opaque to the client and MUST be echoed back
|
|
105
|
+
* verbatim in the next request's `reasoning_details` to continue a signed/encrypted
|
|
106
|
+
* reasoning turn (the upstream rejects an altered or dropped block — e.g. before a tool
|
|
107
|
+
* call). {@link format} tags the provider encoding when known.
|
|
108
|
+
*/
|
|
109
|
+
export interface ReasoningDetail {
|
|
110
|
+
type: string;
|
|
111
|
+
/** set for `"reasoning.text"` */
|
|
112
|
+
text?: string;
|
|
113
|
+
/** set for `"reasoning.summary"` */
|
|
114
|
+
summary?: string;
|
|
115
|
+
/** set for `"reasoning.encrypted"` (hidden) */
|
|
116
|
+
data?: string;
|
|
117
|
+
/** opaque, replayed verbatim */
|
|
118
|
+
signature?: string;
|
|
119
|
+
id?: string;
|
|
120
|
+
/** e.g. "anthropic-claude-v1" */
|
|
121
|
+
format?: string;
|
|
122
|
+
index?: number;
|
|
123
|
+
}
|
|
124
|
+
|
|
66
125
|
export interface ChatMessage {
|
|
67
126
|
role: Role;
|
|
68
127
|
content?: MessageContent;
|
|
@@ -70,6 +129,17 @@ export interface ChatMessage {
|
|
|
70
129
|
toolCalls?: ToolCall[];
|
|
71
130
|
/** set when role == TOOL */
|
|
72
131
|
toolCallId?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Open reasoning text the assistant emitted (OpenRouter `reasoning`), if any. The flat,
|
|
134
|
+
* human-readable form; {@link reasoningDetails} is the structured, replay-safe one.
|
|
135
|
+
*/
|
|
136
|
+
reasoning?: string;
|
|
137
|
+
/**
|
|
138
|
+
* Structured reasoning blocks (open and hidden, with signatures — see
|
|
139
|
+
* {@link ReasoningDetail}). Echo these back verbatim on the next request to preserve
|
|
140
|
+
* signed reasoning across a turn.
|
|
141
|
+
*/
|
|
142
|
+
reasoningDetails?: ReasoningDetail[];
|
|
73
143
|
}
|
|
74
144
|
|
|
75
145
|
export interface FunctionDef {
|
|
@@ -161,6 +231,10 @@ export interface Delta {
|
|
|
161
231
|
/** incremental text */
|
|
162
232
|
content?: string;
|
|
163
233
|
toolCalls?: ToolCallDelta[];
|
|
234
|
+
/** incremental open reasoning text, if any */
|
|
235
|
+
reasoning?: string;
|
|
236
|
+
/** incremental structured reasoning blocks (open / hidden — see {@link ReasoningDetail}). */
|
|
237
|
+
reasoningDetails?: ReasoningDetail[];
|
|
164
238
|
}
|
|
165
239
|
|
|
166
240
|
export interface ChunkChoice {
|
package/src/wire.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
ChatMessage,
|
|
17
17
|
ContentPart,
|
|
18
18
|
MessageContent,
|
|
19
|
+
ReasoningDetail,
|
|
19
20
|
ToolCall,
|
|
20
21
|
ToolDef,
|
|
21
22
|
ToolChoice,
|
|
@@ -27,6 +28,7 @@ import type {
|
|
|
27
28
|
Delta,
|
|
28
29
|
ToolCallDelta,
|
|
29
30
|
Usage,
|
|
31
|
+
PromptTokensDetails,
|
|
30
32
|
EmbeddingRequest,
|
|
31
33
|
EmbeddingResponse,
|
|
32
34
|
Embedding,
|
|
@@ -124,6 +126,22 @@ function encodeToolCall(tc: ToolCall): Json {
|
|
|
124
126
|
};
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Encode one reasoning_details[] entry. `signature` and `data` are opaque and must
|
|
131
|
+
* round-trip verbatim, so they are emitted exactly as given when present.
|
|
132
|
+
*/
|
|
133
|
+
function encodeReasoningDetail(d: ReasoningDetail): Json {
|
|
134
|
+
const out: Json = { type: d.type };
|
|
135
|
+
put(out, "text", d.text);
|
|
136
|
+
put(out, "summary", d.summary);
|
|
137
|
+
put(out, "data", d.data);
|
|
138
|
+
put(out, "signature", d.signature);
|
|
139
|
+
put(out, "id", d.id);
|
|
140
|
+
put(out, "format", d.format);
|
|
141
|
+
put(out, "index", d.index);
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
|
|
127
145
|
function encodeMessage(m: ChatMessage): Json {
|
|
128
146
|
const out: Json = { role: roleToWire(m.role) ?? "user" };
|
|
129
147
|
const content = encodeContent(m.content);
|
|
@@ -135,6 +153,12 @@ function encodeMessage(m: ChatMessage): Json {
|
|
|
135
153
|
out["tool_calls"] = m.toolCalls.map(encodeToolCall);
|
|
136
154
|
}
|
|
137
155
|
put(out, "tool_call_id", m.toolCallId);
|
|
156
|
+
// Reasoning (OpenRouter): echo a prior assistant turn back verbatim to preserve
|
|
157
|
+
// signed/encrypted reasoning across a turn (SPEC.md / ReasoningDetail).
|
|
158
|
+
put(out, "reasoning", m.reasoning);
|
|
159
|
+
if (m.reasoningDetails && m.reasoningDetails.length > 0) {
|
|
160
|
+
out["reasoning_details"] = m.reasoningDetails.map(encodeReasoningDetail);
|
|
161
|
+
}
|
|
138
162
|
return out;
|
|
139
163
|
}
|
|
140
164
|
|
|
@@ -201,6 +225,15 @@ export function encodeChatRequest(req: ChatRequest, forceStream?: boolean): Json
|
|
|
201
225
|
// Chat — decode
|
|
202
226
|
// ===========================================================================
|
|
203
227
|
|
|
228
|
+
function decodePromptTokensDetails(v: unknown): PromptTokensDetails | undefined {
|
|
229
|
+
const o = obj(v);
|
|
230
|
+
if (!o) return undefined;
|
|
231
|
+
const details: PromptTokensDetails = {};
|
|
232
|
+
const cached = optNum(o["cached_tokens"]);
|
|
233
|
+
if (cached !== undefined) details.cachedTokens = cached;
|
|
234
|
+
return details;
|
|
235
|
+
}
|
|
236
|
+
|
|
204
237
|
export function decodeUsage(v: unknown): Usage | undefined {
|
|
205
238
|
const o = obj(v);
|
|
206
239
|
if (!o) return undefined;
|
|
@@ -211,6 +244,10 @@ export function decodeUsage(v: unknown): Usage | undefined {
|
|
|
211
244
|
};
|
|
212
245
|
const cost = optNum(o["cost_usd"]);
|
|
213
246
|
if (cost !== undefined) usage.costUsd = cost;
|
|
247
|
+
const ptd = decodePromptTokensDetails(o["prompt_tokens_details"]);
|
|
248
|
+
if (ptd !== undefined) usage.promptTokensDetails = ptd;
|
|
249
|
+
const cacheCreation = optNum(o["cache_creation_tokens"]);
|
|
250
|
+
if (cacheCreation !== undefined) usage.cacheCreationTokens = cacheCreation;
|
|
214
251
|
return usage;
|
|
215
252
|
}
|
|
216
253
|
|
|
@@ -249,6 +286,34 @@ function decodeToolCall(v: unknown): ToolCall | undefined {
|
|
|
249
286
|
};
|
|
250
287
|
}
|
|
251
288
|
|
|
289
|
+
function decodeReasoningDetail(v: unknown): ReasoningDetail | undefined {
|
|
290
|
+
const o = obj(v);
|
|
291
|
+
if (!o) return undefined;
|
|
292
|
+
const d: ReasoningDetail = { type: str(o["type"]) };
|
|
293
|
+
// `signature` and `data` are opaque blobs replayed verbatim — preserve them as-is.
|
|
294
|
+
const text = optStr(o["text"]);
|
|
295
|
+
if (text !== undefined) d.text = text;
|
|
296
|
+
const summary = optStr(o["summary"]);
|
|
297
|
+
if (summary !== undefined) d.summary = summary;
|
|
298
|
+
const data = optStr(o["data"]);
|
|
299
|
+
if (data !== undefined) d.data = data;
|
|
300
|
+
const signature = optStr(o["signature"]);
|
|
301
|
+
if (signature !== undefined) d.signature = signature;
|
|
302
|
+
const id = optStr(o["id"]);
|
|
303
|
+
if (id !== undefined) d.id = id;
|
|
304
|
+
const format = optStr(o["format"]);
|
|
305
|
+
if (format !== undefined) d.format = format;
|
|
306
|
+
const index = optNum(o["index"]);
|
|
307
|
+
if (index !== undefined) d.index = index;
|
|
308
|
+
return d;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function decodeReasoningDetails(v: unknown): ReasoningDetail[] {
|
|
312
|
+
return arr(v)
|
|
313
|
+
.map(decodeReasoningDetail)
|
|
314
|
+
.filter((x): x is ReasoningDetail => x !== undefined);
|
|
315
|
+
}
|
|
316
|
+
|
|
252
317
|
export function decodeMessage(v: unknown): ChatMessage {
|
|
253
318
|
const o = obj(v) ?? {};
|
|
254
319
|
const msg: ChatMessage = { role: roleFromWire(optStr(o["role"])) };
|
|
@@ -262,6 +327,10 @@ export function decodeMessage(v: unknown): ChatMessage {
|
|
|
262
327
|
if (tcs.length > 0) msg.toolCalls = tcs;
|
|
263
328
|
const tcid = optStr(o["tool_call_id"]);
|
|
264
329
|
if (tcid !== undefined) msg.toolCallId = tcid;
|
|
330
|
+
const reasoning = optStr(o["reasoning"]);
|
|
331
|
+
if (reasoning !== undefined) msg.reasoning = reasoning;
|
|
332
|
+
const rds = decodeReasoningDetails(o["reasoning_details"]);
|
|
333
|
+
if (rds.length > 0) msg.reasoningDetails = rds;
|
|
265
334
|
return msg;
|
|
266
335
|
}
|
|
267
336
|
|
|
@@ -320,6 +389,10 @@ function decodeDelta(v: unknown): Delta {
|
|
|
320
389
|
.map(decodeToolCallDelta)
|
|
321
390
|
.filter((x): x is ToolCallDelta => x !== undefined);
|
|
322
391
|
if (tcs.length > 0) delta.toolCalls = tcs;
|
|
392
|
+
const reasoning = optStr(o["reasoning"]);
|
|
393
|
+
if (reasoning !== undefined) delta.reasoning = reasoning;
|
|
394
|
+
const rds = decodeReasoningDetails(o["reasoning_details"]);
|
|
395
|
+
if (rds.length > 0) delta.reasoningDetails = rds;
|
|
323
396
|
return delta;
|
|
324
397
|
}
|
|
325
398
|
|