mohdel 0.114.1 → 0.116.1
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/js/session/adapters/openai.js +12 -5
- package/js/session/driver.js +31 -10
- package/package.json +12 -12
|
@@ -77,6 +77,7 @@ export async function * openai (envelope, deps = {}) {
|
|
|
77
77
|
let outputTokens = 0
|
|
78
78
|
let thinkingTokens = 0
|
|
79
79
|
let cachedInputTokens = 0
|
|
80
|
+
let cacheWriteTokens = 0
|
|
80
81
|
let status = STATUS_COMPLETED
|
|
81
82
|
/** @type {string | undefined} */
|
|
82
83
|
let warning
|
|
@@ -132,6 +133,7 @@ export async function * openai (envelope, deps = {}) {
|
|
|
132
133
|
outputTokens = event.response.usage.output_tokens ?? 0
|
|
133
134
|
thinkingTokens = event.response.usage.output_tokens_details?.reasoning_tokens ?? 0
|
|
134
135
|
cachedInputTokens = event.response.usage.input_tokens_details?.cached_tokens ?? 0
|
|
136
|
+
cacheWriteTokens = event.response.usage.input_tokens_details?.cache_write_tokens ?? 0
|
|
135
137
|
}
|
|
136
138
|
if (toolItems.size > 0) {
|
|
137
139
|
status = STATUS_TOOL_USE
|
|
@@ -148,6 +150,7 @@ export async function * openai (envelope, deps = {}) {
|
|
|
148
150
|
outputTokens = event.response.usage.output_tokens ?? 0
|
|
149
151
|
thinkingTokens = event.response.usage.output_tokens_details?.reasoning_tokens ?? 0
|
|
150
152
|
cachedInputTokens = event.response.usage.input_tokens_details?.cached_tokens ?? 0
|
|
153
|
+
cacheWriteTokens = event.response.usage.input_tokens_details?.cache_write_tokens ?? 0
|
|
151
154
|
}
|
|
152
155
|
break
|
|
153
156
|
|
|
@@ -177,11 +180,12 @@ export async function * openai (envelope, deps = {}) {
|
|
|
177
180
|
// one from the other for the message-only count.
|
|
178
181
|
const messageOutputTokens = Math.max(0, outputTokens - thinkingTokens)
|
|
179
182
|
|
|
180
|
-
// OpenAI counts cached_tokens as
|
|
181
|
-
// mohdel's additive convention (
|
|
182
|
-
// inputTokens) by subtracting
|
|
183
|
-
//
|
|
184
|
-
|
|
183
|
+
// OpenAI counts cached_tokens and cache_write_tokens as SUBSETS of
|
|
184
|
+
// input_tokens. Convert to mohdel's additive convention (cacheRead/
|
|
185
|
+
// cacheWriteInputTokens are separate from inputTokens) by subtracting
|
|
186
|
+
// both portions before pricing. Both adapters and computeCost stay
|
|
187
|
+
// simpler with the additive shape.
|
|
188
|
+
const regularInputTokens = Math.max(0, inputTokens - cachedInputTokens - cacheWriteTokens)
|
|
185
189
|
|
|
186
190
|
/** @type {import('#core/events.js').DoneEvent} */
|
|
187
191
|
const done = {
|
|
@@ -192,6 +196,7 @@ export async function * openai (envelope, deps = {}) {
|
|
|
192
196
|
inputTokens: regularInputTokens,
|
|
193
197
|
outputTokens: messageOutputTokens,
|
|
194
198
|
thinkingTokens,
|
|
199
|
+
...(cacheWriteTokens > 0 && { cacheWriteInputTokens: cacheWriteTokens }),
|
|
195
200
|
...(cachedInputTokens > 0 && { cacheReadInputTokens: cachedInputTokens }),
|
|
196
201
|
cost: costFor(
|
|
197
202
|
catalogKey(envelope.model),
|
|
@@ -199,6 +204,7 @@ export async function * openai (envelope, deps = {}) {
|
|
|
199
204
|
inputTokens: regularInputTokens,
|
|
200
205
|
outputTokens: messageOutputTokens,
|
|
201
206
|
thinkingTokens,
|
|
207
|
+
cacheWriteInputTokens: cacheWriteTokens,
|
|
202
208
|
cacheReadInputTokens: cachedInputTokens
|
|
203
209
|
}
|
|
204
210
|
),
|
|
@@ -273,6 +279,7 @@ function buildRequest (envelope, input, instructions) {
|
|
|
273
279
|
if (envelope.identifier) {
|
|
274
280
|
if (provider === 'openai') {
|
|
275
281
|
request.safety_identifier = envelope.identifier
|
|
282
|
+
request.prompt_cache_key = envelope.identifier
|
|
276
283
|
} else {
|
|
277
284
|
request.user = envelope.identifier
|
|
278
285
|
}
|
package/js/session/driver.js
CHANGED
|
@@ -12,8 +12,6 @@
|
|
|
12
12
|
* @module session/driver
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import readline from 'node:readline'
|
|
16
|
-
|
|
17
15
|
import { run } from './run.js'
|
|
18
16
|
import { runImage } from './run_image.js'
|
|
19
17
|
import { runTranscription } from './run_transcription.js'
|
|
@@ -29,8 +27,6 @@ const PRECANCEL_CAP = 128
|
|
|
29
27
|
* @returns {Promise<void>}
|
|
30
28
|
*/
|
|
31
29
|
export async function drive (stdin, stdout) {
|
|
32
|
-
const rl = readline.createInterface({ input: stdin, crlfDelay: Infinity })
|
|
33
|
-
|
|
34
30
|
/** @type {{callId: string, controller: AbortController} | null} */
|
|
35
31
|
let currentCall = null
|
|
36
32
|
/** @type {import('#core/envelope.js').CallEnvelope[]} */
|
|
@@ -38,6 +34,7 @@ export async function drive (stdin, stdout) {
|
|
|
38
34
|
/** @type {(() => void) | null} */
|
|
39
35
|
let queueNotify = null
|
|
40
36
|
let stdinClosed = false
|
|
37
|
+
let framingError = null
|
|
41
38
|
/** Cancel messages received before their envelope was dequeued.
|
|
42
39
|
* JS Sets are insertion-ordered, so `values().next()` is the
|
|
43
40
|
* oldest entry — cheap FIFO eviction at cap. */
|
|
@@ -51,7 +48,8 @@ export async function drive (stdin, stdout) {
|
|
|
51
48
|
precancelled.add(callId)
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
|
|
51
|
+
const onLine = (line) => {
|
|
52
|
+
if (framingError) return
|
|
55
53
|
const trimmed = line.trim()
|
|
56
54
|
if (!trimmed) return
|
|
57
55
|
|
|
@@ -59,7 +57,17 @@ export async function drive (stdin, stdout) {
|
|
|
59
57
|
try {
|
|
60
58
|
obj = JSON.parse(trimmed)
|
|
61
59
|
} catch (e) {
|
|
62
|
-
process.stderr.write(`session:
|
|
60
|
+
process.stderr.write(`session: malformed stdin line, exiting: ${e.message}\n`)
|
|
61
|
+
const error = {
|
|
62
|
+
message: 'SESSION_STDIN_MALFORMED',
|
|
63
|
+
detail: `stdin line is not valid JSON: ${e.message}`,
|
|
64
|
+
severity: 'error',
|
|
65
|
+
retryable: false,
|
|
66
|
+
type: 'SESSION_STDIN_MALFORMED'
|
|
67
|
+
}
|
|
68
|
+
stdout.write(JSON.stringify({ type: 'error', error }) + '\n')
|
|
69
|
+
framingError = Object.assign(new Error(error.message), { detail: error.detail })
|
|
70
|
+
if (queueNotify) { queueNotify(); queueNotify = null }
|
|
63
71
|
return
|
|
64
72
|
}
|
|
65
73
|
|
|
@@ -113,22 +121,35 @@ export async function drive (stdin, stdout) {
|
|
|
113
121
|
|
|
114
122
|
envelopeQueue.push(obj)
|
|
115
123
|
if (queueNotify) { queueNotify(); queueNotify = null }
|
|
116
|
-
}
|
|
124
|
+
}
|
|
117
125
|
|
|
118
|
-
|
|
126
|
+
let stdinBuf = ''
|
|
127
|
+
stdin.setEncoding('utf8')
|
|
128
|
+
stdin.on('data', (chunk) => {
|
|
129
|
+
stdinBuf += chunk
|
|
130
|
+
let nl
|
|
131
|
+
while ((nl = stdinBuf.indexOf('\n')) !== -1) {
|
|
132
|
+
const line = stdinBuf.slice(0, nl)
|
|
133
|
+
stdinBuf = stdinBuf.slice(nl + 1)
|
|
134
|
+
onLine(line)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
stdin.on('end', () => {
|
|
138
|
+
if (stdinBuf) onLine(stdinBuf)
|
|
119
139
|
stdinClosed = true
|
|
120
140
|
if (queueNotify) { queueNotify(); queueNotify = null }
|
|
121
141
|
})
|
|
122
142
|
|
|
123
143
|
while (true) {
|
|
124
|
-
// `stdinClosed` flips asynchronously inside the `
|
|
144
|
+
// `stdinClosed` flips asynchronously inside the `stdin.on('end')`
|
|
125
145
|
// callback above, which also signals `queueNotify`. The linter's
|
|
126
146
|
// no-unmodified-loop-condition rule can't see callback mutation,
|
|
127
147
|
// so it false-positives here.
|
|
128
148
|
// eslint-disable-next-line no-unmodified-loop-condition
|
|
129
|
-
while (envelopeQueue.length === 0 && !stdinClosed) {
|
|
149
|
+
while (envelopeQueue.length === 0 && !stdinClosed && !framingError) {
|
|
130
150
|
await new Promise(resolve => { queueNotify = resolve })
|
|
131
151
|
}
|
|
152
|
+
if (framingError) throw framingError
|
|
132
153
|
if (envelopeQueue.length === 0) return
|
|
133
154
|
|
|
134
155
|
const envelope = envelopeQueue.shift()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mohdel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.116.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Christophe Le Bars",
|
|
@@ -83,20 +83,20 @@
|
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
85
|
"optionalDependencies": {
|
|
86
|
-
"@clack/prompts": "^1.
|
|
87
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
88
|
-
"@opentelemetry/sdk-node": "^0.
|
|
86
|
+
"@clack/prompts": "^1.7.0",
|
|
87
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.220.0",
|
|
88
|
+
"@opentelemetry/sdk-node": "^0.220.0",
|
|
89
89
|
"chalk": "^5.4.0",
|
|
90
|
-
"mohdel-thin-gate-linux-x64-gnu": "0.
|
|
90
|
+
"mohdel-thin-gate-linux-x64-gnu": "0.116.1"
|
|
91
91
|
},
|
|
92
92
|
"dependencies": {
|
|
93
|
-
"@anthropic-ai/sdk": "^0.
|
|
93
|
+
"@anthropic-ai/sdk": "^0.110.0",
|
|
94
94
|
"@cerebras/cerebras_cloud_sdk": "^1.61.1",
|
|
95
|
-
"@google/genai": "^2.
|
|
95
|
+
"@google/genai": "^2.11.0",
|
|
96
96
|
"@opentelemetry/api": "^1.9.1",
|
|
97
97
|
"env-paths": "^4.0.0",
|
|
98
|
-
"groq-sdk": "^1.
|
|
99
|
-
"openai": "^6.
|
|
98
|
+
"groq-sdk": "^1.3.0",
|
|
99
|
+
"openai": "^6.46.0",
|
|
100
100
|
"undici": "^7.24.5"
|
|
101
101
|
},
|
|
102
102
|
"lint-staged": {
|
|
@@ -104,9 +104,9 @@
|
|
|
104
104
|
},
|
|
105
105
|
"devDependencies": {
|
|
106
106
|
"gpt-tokenizer": "^3.4.0",
|
|
107
|
-
"lint-staged": "^17.0.
|
|
108
|
-
"release-it": "^20.2.
|
|
107
|
+
"lint-staged": "^17.0.8",
|
|
108
|
+
"release-it": "^20.2.1",
|
|
109
109
|
"standard": "^17.1.2",
|
|
110
|
-
"vitest": "^4.1.
|
|
110
|
+
"vitest": "^4.1.10"
|
|
111
111
|
}
|
|
112
112
|
}
|