mohdel 1.5.0 → 1.6.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 +1 -1
- package/js/client/call.js +5 -2
- package/js/client/call_embedding.js +5 -2
- package/js/client/call_image.js +5 -2
- package/js/client/call_transcription.js +5 -2
- package/js/session/run.js +34 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -355,7 +355,7 @@ With no session-bin configured, thin-gate runs in demo mode: `POST /v1/call` ret
|
|
|
355
355
|
|
|
356
356
|
### Calling from JS
|
|
357
357
|
|
|
358
|
-
The client snippet under [Library Usage](#library-usage) above is the full surface: `call(envelope, { socketPath, signal? })` returns an async iterable of events. Pass an `AbortSignal` to cancel in flight; thin-gate forwards a cancel control message to the session and reuses it on the pool, and the client ends the stream with the same cancelled `done` the in-process path returns (status `incomplete`, warning `cancelled`, the partial output it relayed, zero tokens). The envelope is the flat `answer(prompt, options)` surface plus transport metadata (`callId`, `authId`, `auth.key`, optional `traceparent`); see [`js/core/envelope.js`](js/core/envelope.js) for the full field list.
|
|
358
|
+
The client snippet under [Library Usage](#library-usage) above is the full surface: `call(envelope, { socketPath, signal?, headers? })` returns an async iterable of events. `headers` go with the request, for a router in front of the gate that authenticates its callers. Pass an `AbortSignal` to cancel in flight; thin-gate forwards a cancel control message to the session and reuses it on the pool, and the client ends the stream with the same cancelled `done` the in-process path returns (status `incomplete`, warning `cancelled`, the partial output it relayed, zero tokens). The envelope is the flat `answer(prompt, options)` surface plus transport metadata (`callId`, `authId`, `auth.key`, optional `traceparent`); see [`js/core/envelope.js`](js/core/envelope.js) for the full field list.
|
|
359
359
|
|
|
360
360
|
### Other languages
|
|
361
361
|
|
package/js/client/call.js
CHANGED
|
@@ -44,9 +44,11 @@ function cancelledDone (start, first, output) {
|
|
|
44
44
|
* @param {string} options.socketPath
|
|
45
45
|
* @param {AbortSignal} [options.signal]
|
|
46
46
|
* @param {string} [options.path] HTTP path; defaults to '/v1/call'
|
|
47
|
+
* @param {Record<string, string>} [options.headers] sent with the request, for a router in front of the gate;
|
|
48
|
+
* `content-type`, `content-length`, `transfer-encoding`, `connection` and `host` are the transport's
|
|
47
49
|
* @returns {AsyncGenerator<import('#core/events.js').Event>}
|
|
48
50
|
*/
|
|
49
|
-
export async function * call (envelope, { socketPath, signal, path = '/v1/call' }) {
|
|
51
|
+
export async function * call (envelope, { socketPath, signal, path = '/v1/call', headers }) {
|
|
50
52
|
const start = String(process.hrtime.bigint())
|
|
51
53
|
if (signal?.aborted) {
|
|
52
54
|
yield cancelledDone(start, null, '')
|
|
@@ -60,7 +62,8 @@ export async function * call (envelope, { socketPath, signal, path = '/v1/call'
|
|
|
60
62
|
path,
|
|
61
63
|
method: 'POST',
|
|
62
64
|
body: envelope,
|
|
63
|
-
signal
|
|
65
|
+
signal,
|
|
66
|
+
headers
|
|
64
67
|
})
|
|
65
68
|
} catch (e) {
|
|
66
69
|
if (signal?.aborted) {
|
|
@@ -19,15 +19,18 @@ import { MohdelError } from '#core'
|
|
|
19
19
|
* @param {string} options.socketPath
|
|
20
20
|
* @param {AbortSignal} [options.signal]
|
|
21
21
|
* @param {string} [options.path] HTTP path; defaults to '/v1/embed'
|
|
22
|
+
* @param {Record<string, string>} [options.headers] sent with the request, for a router in front of the gate;
|
|
23
|
+
* `content-type`, `content-length`, `transfer-encoding`, `connection` and `host` are the transport's
|
|
22
24
|
* @returns {Promise<import('#core/embedding.js').EmbedResult>}
|
|
23
25
|
*/
|
|
24
|
-
export async function callEmbedding (envelope, { socketPath, signal, path = '/v1/embed' }) {
|
|
26
|
+
export async function callEmbedding (envelope, { socketPath, signal, path = '/v1/embed', headers }) {
|
|
25
27
|
const res = await requestUnix({
|
|
26
28
|
socketPath,
|
|
27
29
|
path,
|
|
28
30
|
method: 'POST',
|
|
29
31
|
body: envelope,
|
|
30
|
-
signal
|
|
32
|
+
signal,
|
|
33
|
+
headers
|
|
31
34
|
})
|
|
32
35
|
|
|
33
36
|
const body = await readAll(res)
|
package/js/client/call_image.js
CHANGED
|
@@ -17,15 +17,18 @@ import { MohdelError } from '#core'
|
|
|
17
17
|
* @param {string} options.socketPath
|
|
18
18
|
* @param {AbortSignal} [options.signal]
|
|
19
19
|
* @param {string} [options.path] HTTP path; defaults to '/v1/image'
|
|
20
|
+
* @param {Record<string, string>} [options.headers] sent with the request, for a router in front of the gate;
|
|
21
|
+
* `content-type`, `content-length`, `transfer-encoding`, `connection` and `host` are the transport's
|
|
20
22
|
* @returns {Promise<import('#core/image.js').ImageResult>}
|
|
21
23
|
*/
|
|
22
|
-
export async function callImage (envelope, { socketPath, signal, path = '/v1/image' }) {
|
|
24
|
+
export async function callImage (envelope, { socketPath, signal, path = '/v1/image', headers }) {
|
|
23
25
|
const res = await requestUnix({
|
|
24
26
|
socketPath,
|
|
25
27
|
path,
|
|
26
28
|
method: 'POST',
|
|
27
29
|
body: envelope,
|
|
28
|
-
signal
|
|
30
|
+
signal,
|
|
31
|
+
headers
|
|
29
32
|
})
|
|
30
33
|
|
|
31
34
|
const body = await readAll(res)
|
|
@@ -20,15 +20,18 @@ import { MohdelError } from '#core'
|
|
|
20
20
|
* @param {string} options.socketPath
|
|
21
21
|
* @param {AbortSignal} [options.signal]
|
|
22
22
|
* @param {string} [options.path] HTTP path; defaults to '/v1/transcription'
|
|
23
|
+
* @param {Record<string, string>} [options.headers] sent with the request, for a router in front of the gate;
|
|
24
|
+
* `content-type`, `content-length`, `transfer-encoding`, `connection` and `host` are the transport's
|
|
23
25
|
* @returns {Promise<import('#core/transcription.js').TranscriptionResult>}
|
|
24
26
|
*/
|
|
25
|
-
export async function callTranscription (envelope, { socketPath, signal, path = '/v1/transcription' }) {
|
|
27
|
+
export async function callTranscription (envelope, { socketPath, signal, path = '/v1/transcription', headers }) {
|
|
26
28
|
const res = await requestUnix({
|
|
27
29
|
socketPath,
|
|
28
30
|
path,
|
|
29
31
|
method: 'POST',
|
|
30
32
|
body: envelope,
|
|
31
|
-
signal
|
|
33
|
+
signal,
|
|
34
|
+
headers
|
|
32
35
|
})
|
|
33
36
|
|
|
34
37
|
const body = await readAll(res)
|
package/js/session/run.js
CHANGED
|
@@ -85,6 +85,9 @@ export async function * run (envelope, {
|
|
|
85
85
|
if (norm.error) { yield norm.error; return }
|
|
86
86
|
envelope = norm.envelope
|
|
87
87
|
|
|
88
|
+
const cacheErr = cacheMarkerError(envelope.prompt)
|
|
89
|
+
if (cacheErr) { yield cacheErr; return }
|
|
90
|
+
|
|
88
91
|
const provider = providerOf(envelope.model)
|
|
89
92
|
const span = openSpan(envelope)
|
|
90
93
|
const log = scopedLogger(logger, envelope, span)
|
|
@@ -357,6 +360,37 @@ export function normalizeModelId (envelope, resolveSpec) {
|
|
|
357
360
|
return { envelope: next, key: base, spec: baseSpec }
|
|
358
361
|
}
|
|
359
362
|
|
|
363
|
+
/**
|
|
364
|
+
* A `cache` marker is honoured on `text` parts only, with a TTL the
|
|
365
|
+
* adapters know. The adapters would ignore any other marker, so it is
|
|
366
|
+
* refused rather than dropped.
|
|
367
|
+
*
|
|
368
|
+
* @param {import('#core/envelope.js').CallEnvelope['prompt']} prompt
|
|
369
|
+
* @returns {import('#core/events.js').ErrorEvent | undefined}
|
|
370
|
+
*/
|
|
371
|
+
function cacheMarkerError (prompt) {
|
|
372
|
+
if (!Array.isArray(prompt)) return undefined
|
|
373
|
+
for (const m of prompt) {
|
|
374
|
+
if (!Array.isArray(m?.content)) continue
|
|
375
|
+
for (const p of m.content) {
|
|
376
|
+
if (p?.cache == null) continue
|
|
377
|
+
if (p.type !== 'text') {
|
|
378
|
+
return errorEvent(
|
|
379
|
+
`\`cache\` is accepted on text parts only, not on a ${p.type} part (${m.role} message)`,
|
|
380
|
+
'SESSION_INVALID_PROMPT'
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
if (p.cache !== '5m' && p.cache !== '1h') {
|
|
384
|
+
return errorEvent(
|
|
385
|
+
`\`cache\` on a text part must be '5m' or '1h' (got ${JSON.stringify(p.cache)})`,
|
|
386
|
+
'SESSION_INVALID_PROMPT'
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return undefined
|
|
392
|
+
}
|
|
393
|
+
|
|
360
394
|
/**
|
|
361
395
|
* The two lane guards, in caller-then-internal order. Returns an
|
|
362
396
|
* error event, or `undefined` when the lane is usable.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mohdel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Christophe Le Bars",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"@opentelemetry/exporter-trace-otlp-grpc": "^0.222.0",
|
|
137
137
|
"@opentelemetry/sdk-node": "^0.222.0",
|
|
138
138
|
"chalk": "^6.0.0",
|
|
139
|
-
"mohdel-thin-gate-linux-x64-gnu": "1.
|
|
139
|
+
"mohdel-thin-gate-linux-x64-gnu": "1.6.0"
|
|
140
140
|
},
|
|
141
141
|
"dependencies": {
|
|
142
142
|
"@anthropic-ai/sdk": "^0.128.0",
|