mohdel 0.90.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/LICENSE +21 -0
- package/README.md +377 -0
- package/config/benchmarks.json +39 -0
- package/js/client/call.js +75 -0
- package/js/client/call_image.js +82 -0
- package/js/client/gate-binary.js +72 -0
- package/js/client/index.js +16 -0
- package/js/client/ndjson.js +29 -0
- package/js/client/transport.js +48 -0
- package/js/core/envelope.js +141 -0
- package/js/core/errors.js +75 -0
- package/js/core/events.js +96 -0
- package/js/core/image.js +58 -0
- package/js/core/index.js +10 -0
- package/js/core/status.js +48 -0
- package/js/factory/bridge.js +372 -0
- package/js/session/_cooldown.js +114 -0
- package/js/session/_logger.js +138 -0
- package/js/session/_rate_limiter.js +77 -0
- package/js/session/_tracing.js +58 -0
- package/js/session/adapters/_cancelled.js +44 -0
- package/js/session/adapters/_catalog.js +58 -0
- package/js/session/adapters/_chat_completions.js +439 -0
- package/js/session/adapters/_errors.js +85 -0
- package/js/session/adapters/_images.js +60 -0
- package/js/session/adapters/_lazy_json_cache.js +76 -0
- package/js/session/adapters/_pricing.js +67 -0
- package/js/session/adapters/_providers.js +60 -0
- package/js/session/adapters/_tools.js +185 -0
- package/js/session/adapters/_videos.js +283 -0
- package/js/session/adapters/anthropic.js +397 -0
- package/js/session/adapters/cerebras.js +28 -0
- package/js/session/adapters/deepseek.js +32 -0
- package/js/session/adapters/echo.js +51 -0
- package/js/session/adapters/fake.js +262 -0
- package/js/session/adapters/fireworks.js +46 -0
- package/js/session/adapters/gemini.js +381 -0
- package/js/session/adapters/groq.js +23 -0
- package/js/session/adapters/image/fake.js +55 -0
- package/js/session/adapters/image/index.js +40 -0
- package/js/session/adapters/image/novita.js +135 -0
- package/js/session/adapters/image/openai.js +50 -0
- package/js/session/adapters/index.js +53 -0
- package/js/session/adapters/mistral.js +31 -0
- package/js/session/adapters/novita.js +29 -0
- package/js/session/adapters/openai.js +381 -0
- package/js/session/adapters/openrouter.js +66 -0
- package/js/session/adapters/xai.js +27 -0
- package/js/session/bin.js +54 -0
- package/js/session/driver.js +160 -0
- package/js/session/index.js +18 -0
- package/js/session/run.js +393 -0
- package/js/session/run_image.js +61 -0
- package/package.json +107 -0
- package/src/cli/ask.js +160 -0
- package/src/cli/backup.js +107 -0
- package/src/cli/bench.js +262 -0
- package/src/cli/check.js +123 -0
- package/src/cli/colored-logger.js +67 -0
- package/src/cli/colors.js +13 -0
- package/src/cli/default.js +39 -0
- package/src/cli/index.js +150 -0
- package/src/cli/json-output.js +60 -0
- package/src/cli/model.js +571 -0
- package/src/cli/onboard.js +232 -0
- package/src/cli/rank.js +176 -0
- package/src/cli/ratelimit.js +160 -0
- package/src/cli/tag.js +105 -0
- package/src/lib/assets/alibaba.svg +1 -0
- package/src/lib/assets/anthropic.svg +5 -0
- package/src/lib/assets/deepseek.svg +1 -0
- package/src/lib/assets/gemini.svg +1 -0
- package/src/lib/assets/google.svg +2 -0
- package/src/lib/assets/kwaipilot.svg +1 -0
- package/src/lib/assets/meta.svg +1 -0
- package/src/lib/assets/minimax.svg +9 -0
- package/src/lib/assets/moonshotai.svg +4 -0
- package/src/lib/assets/openai.svg +5 -0
- package/src/lib/assets/xai.svg +1 -0
- package/src/lib/assets/xiaomi.svg +2 -0
- package/src/lib/assets/zai.svg +219 -0
- package/src/lib/benchmark-score.js +215 -0
- package/src/lib/benchmark-truth.js +68 -0
- package/src/lib/cache.js +76 -0
- package/src/lib/common.js +208 -0
- package/src/lib/cooldown.js +63 -0
- package/src/lib/creators.js +71 -0
- package/src/lib/curated-cache.js +146 -0
- package/src/lib/errors.js +126 -0
- package/src/lib/index.js +726 -0
- package/src/lib/logger.js +29 -0
- package/src/lib/providers.js +87 -0
- package/src/lib/rank.js +390 -0
- package/src/lib/rate-limiter.js +50 -0
- package/src/lib/schema.js +150 -0
- package/src/lib/select.js +474 -0
- package/src/lib/tracing.js +62 -0
- package/src/lib/utils.js +85 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mohdel client — thin caller for thin-gate's data-plane unix socket.
|
|
3
|
+
*
|
|
4
|
+
* Public surface (0.90):
|
|
5
|
+
* - call(envelope, { socketPath, signal }): AsyncGenerator<Event>
|
|
6
|
+
* - callImage(envelope, { socketPath, signal }): Promise<ImageResult>
|
|
7
|
+
*
|
|
8
|
+
* No provider SDKs are imported transitively. This module can be
|
|
9
|
+
* consumed by callers that must not pull openai-node, anthropic-sdk,
|
|
10
|
+
* etc. into their bundle.
|
|
11
|
+
*
|
|
12
|
+
* @module client
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export { call } from './call.js'
|
|
16
|
+
export { callImage } from './call_image.js'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NDJSON line parser. Yields parsed objects from a byte/string stream.
|
|
3
|
+
*
|
|
4
|
+
* @module client/ndjson
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const MAX_LINE_BYTES = 16 * 1024 * 1024
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {AsyncIterable<Buffer|string>} stream
|
|
11
|
+
* @returns {AsyncGenerator<unknown>}
|
|
12
|
+
*/
|
|
13
|
+
export async function * parseNDJSON (stream) {
|
|
14
|
+
let buf = ''
|
|
15
|
+
for await (const chunk of stream) {
|
|
16
|
+
buf += typeof chunk === 'string' ? chunk : chunk.toString('utf8')
|
|
17
|
+
if (buf.length > MAX_LINE_BYTES) {
|
|
18
|
+
throw new Error(`NDJSON line exceeds ${MAX_LINE_BYTES} bytes without newline`)
|
|
19
|
+
}
|
|
20
|
+
let nl
|
|
21
|
+
while ((nl = buf.indexOf('\n')) !== -1) {
|
|
22
|
+
const line = buf.slice(0, nl).trim()
|
|
23
|
+
buf = buf.slice(nl + 1)
|
|
24
|
+
if (line) yield JSON.parse(line)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const tail = buf.trim()
|
|
28
|
+
if (tail) yield JSON.parse(tail)
|
|
29
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level unix-socket HTTP transport for mohdel client.
|
|
3
|
+
*
|
|
4
|
+
* Stateless — each request opens and closes its own socket. Pooling/
|
|
5
|
+
* keep-alive can be added later if measurement shows a bottleneck;
|
|
6
|
+
* unix-socket overhead is typically negligible.
|
|
7
|
+
*
|
|
8
|
+
* @module client/transport
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import http from 'node:http'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {object} RequestOptions
|
|
15
|
+
* @property {string} socketPath
|
|
16
|
+
* @property {string} path
|
|
17
|
+
* @property {string} method
|
|
18
|
+
* @property {object} [body]
|
|
19
|
+
* @property {AbortSignal} [signal]
|
|
20
|
+
* @property {Record<string,string>} [headers]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {RequestOptions} options
|
|
25
|
+
* @returns {Promise<http.IncomingMessage>}
|
|
26
|
+
*/
|
|
27
|
+
export function requestUnix ({ socketPath, path, method, body, signal, headers }) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
/** @type {Record<string,string>} */
|
|
30
|
+
const h = { ...(headers || {}) }
|
|
31
|
+
if (body !== undefined) h['content-type'] = 'application/json'
|
|
32
|
+
|
|
33
|
+
const req = http.request({ socketPath, path, method, headers: h }, resolve)
|
|
34
|
+
req.on('error', reject)
|
|
35
|
+
|
|
36
|
+
if (signal) {
|
|
37
|
+
if (signal.aborted) {
|
|
38
|
+
req.destroy(new Error('aborted'))
|
|
39
|
+
reject(new Error('aborted'))
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
signal.addEventListener('abort', () => req.destroy(new Error('aborted')), { once: true })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (body !== undefined) req.end(JSON.stringify(body))
|
|
46
|
+
else req.end()
|
|
47
|
+
})
|
|
48
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical call envelope — the frozen wire shape for a single
|
|
3
|
+
* inference call. Flat `answer(prompt, options)` surface plus the
|
|
4
|
+
* minimum transport metadata needed to cross a process boundary
|
|
5
|
+
* (callId, authId, auth.key, traceparent).
|
|
6
|
+
*
|
|
7
|
+
* camelCase on the wire. JSON on NDJSON frames. Rust mirror:
|
|
8
|
+
* `rust/thin-gate/src/protocol.rs`.
|
|
9
|
+
*
|
|
10
|
+
* @module core/envelope
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {object} CallEnvelope
|
|
15
|
+
*
|
|
16
|
+
* @property {string} callId
|
|
17
|
+
* Unique per call.
|
|
18
|
+
* @property {string} authId
|
|
19
|
+
* Quota-scoping identity (rate-limit/cooldown bucket). Distinct
|
|
20
|
+
* from `identifier`, which is the provider-side safety/tracking
|
|
21
|
+
* ID that gets forwarded to the provider API.
|
|
22
|
+
* @property {Auth} auth
|
|
23
|
+
* @property {string} [traceparent] W3C tracecontext header.
|
|
24
|
+
* @property {string} [baggage] W3C baggage header.
|
|
25
|
+
*
|
|
26
|
+
* @property {string} provider Adapter discriminator.
|
|
27
|
+
* @property {string} model Provider-native model id.
|
|
28
|
+
*
|
|
29
|
+
* @property {(string|Message[])} prompt
|
|
30
|
+
* Either a plain string or a structured array of messages.
|
|
31
|
+
*
|
|
32
|
+
* --- Answer options (flat) ---
|
|
33
|
+
*
|
|
34
|
+
* @property {number} [outputBudget]
|
|
35
|
+
* Max output tokens (clamped to model's outputTokenLimit).
|
|
36
|
+
* @property {('text'|'json')} [outputType]
|
|
37
|
+
* Default 'text'.
|
|
38
|
+
* @property {('chat'|'coding'|'analysis'|'translation'|'creative')} [outputStyle]
|
|
39
|
+
* @property {string} [outputEffort]
|
|
40
|
+
* Thinking effort level. Valid keys are per-model — validated at
|
|
41
|
+
* runtime against the curated entry's `thinkingEffortLevels`.
|
|
42
|
+
* Default is the model's `defaultThinkingEffort`.
|
|
43
|
+
*
|
|
44
|
+
* @property {MediaRef[]} [images]
|
|
45
|
+
* @property {MediaRef[]} [videos]
|
|
46
|
+
* @property {boolean} [cache]
|
|
47
|
+
* Cache uploaded files (Gemini videos).
|
|
48
|
+
*
|
|
49
|
+
* @property {ToolSpec[]} [tools]
|
|
50
|
+
* @property {('auto'|'required'|'none'|string)} [toolChoice]
|
|
51
|
+
* @property {boolean} [parallelToolCalls]
|
|
52
|
+
*
|
|
53
|
+
* @property {string} [identifier]
|
|
54
|
+
* Opaque per-user ID passed to the provider for tracking / abuse
|
|
55
|
+
* monitoring.
|
|
56
|
+
*
|
|
57
|
+
* @property {Object<string, object>} [providerOptions]
|
|
58
|
+
* Namespaced bag of provider-specific knobs that don't fit the
|
|
59
|
+
* shared envelope. Keys are provider names; values are arbitrary
|
|
60
|
+
* JSON objects the matching adapter consumes. Today only
|
|
61
|
+
* `providerOptions.openrouter = {order?, allow?, deny?}` is
|
|
62
|
+
* recognized (OpenRouter routing preferences). Unknown keys are
|
|
63
|
+
* accepted on the wire and silently ignored by adapters that
|
|
64
|
+
* don't read them — upgrade to a typed sub-struct per provider if
|
|
65
|
+
* strict validation becomes necessary.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {object} Auth
|
|
70
|
+
* @property {string} key Provider API key. Redact in logs; never persist.
|
|
71
|
+
* @property {string} [baseURL]
|
|
72
|
+
* Optional override of the adapter's default provider endpoint.
|
|
73
|
+
* Lets callers point at a self-hosted deployment, regional endpoint,
|
|
74
|
+
* proxy, or test server. Adapters treat it as `baseURL ?? ADAPTER_DEFAULT`.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @typedef {object} Message
|
|
79
|
+
* @property {('system'|'user'|'assistant'|'tool')} role
|
|
80
|
+
* @property {(string|MessagePart[])} content
|
|
81
|
+
* @property {string} [toolCallId]
|
|
82
|
+
* Set on `tool` messages — identifies which assistant tool call
|
|
83
|
+
* this is a response to.
|
|
84
|
+
* @property {string} [name]
|
|
85
|
+
* Optional function name for `tool` messages (providers that
|
|
86
|
+
* require naming the tool alongside the response).
|
|
87
|
+
* @property {ToolCall[]} [toolCalls]
|
|
88
|
+
* Set on `assistant` messages when the model called tools in this
|
|
89
|
+
* turn. Preserves multi-turn histories where the assistant both
|
|
90
|
+
* spoke text AND invoked tools; adapters emit the provider-native
|
|
91
|
+
* tool_use / function_call / tool_calls representation.
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @typedef {object} ToolCall
|
|
96
|
+
* @property {string} id
|
|
97
|
+
* @property {string} name
|
|
98
|
+
* @property {object} arguments Parsed object, not a JSON string.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @typedef {object} MessagePart
|
|
103
|
+
* @property {('text'|'reasoning')} type
|
|
104
|
+
* @property {string} text
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @typedef {object} MediaRef
|
|
109
|
+
* @property {string} fileUri
|
|
110
|
+
* @property {string} mimeType
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @typedef {object} ToolSpec
|
|
115
|
+
* @property {string} name
|
|
116
|
+
* @property {string} [description]
|
|
117
|
+
* @property {object} parameters JSON Schema
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
export const ENVELOPE_FIELDS = Object.freeze([
|
|
121
|
+
'callId',
|
|
122
|
+
'authId',
|
|
123
|
+
'auth',
|
|
124
|
+
'traceparent',
|
|
125
|
+
'baggage',
|
|
126
|
+
'provider',
|
|
127
|
+
'model',
|
|
128
|
+
'prompt',
|
|
129
|
+
'outputBudget',
|
|
130
|
+
'outputType',
|
|
131
|
+
'outputStyle',
|
|
132
|
+
'outputEffort',
|
|
133
|
+
'images',
|
|
134
|
+
'videos',
|
|
135
|
+
'cache',
|
|
136
|
+
'tools',
|
|
137
|
+
'toolChoice',
|
|
138
|
+
'parallelToolCalls',
|
|
139
|
+
'identifier',
|
|
140
|
+
'providerOptions'
|
|
141
|
+
])
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `TypedError` — wire-format error. Carries `message` (machine key),
|
|
3
|
+
* optional `detail` (user-facing context), `severity` (lowercase
|
|
4
|
+
* string), `retryable`, and optional `type` (canonical tag).
|
|
5
|
+
*
|
|
6
|
+
* Rust mirror: `rust/thin-gate/src/protocol.rs::TypedError`.
|
|
7
|
+
*
|
|
8
|
+
* @module core/errors
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {('trace'|'debug'|'info'|'warn'|'error'|'fatal')} SeverityTag
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} TypedError
|
|
17
|
+
* @property {string} message
|
|
18
|
+
* Top-level message. Never echo provider response bodies.
|
|
19
|
+
* @property {string} [detail]
|
|
20
|
+
* User-facing error detail (mirrors `MohdelError.detail`).
|
|
21
|
+
* @property {SeverityTag} severity
|
|
22
|
+
* @property {boolean} retryable
|
|
23
|
+
* @property {string} [type]
|
|
24
|
+
* Optional canonical tag (e.g. `'PROVIDER_COOLDOWN'`, `'AUTH_INVALID'`).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export const SEVERITY_TAGS = Object.freeze([
|
|
28
|
+
'trace', 'debug', 'info', 'warn', 'error', 'fatal'
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
export class MohdelTypedError extends Error {
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} message
|
|
34
|
+
* @param {{
|
|
35
|
+
* severity?: SeverityTag,
|
|
36
|
+
* retryable?: boolean,
|
|
37
|
+
* detail?: string,
|
|
38
|
+
* type?: string
|
|
39
|
+
* }} [options]
|
|
40
|
+
*/
|
|
41
|
+
constructor (message, { severity = 'error', retryable = false, detail, type } = {}) {
|
|
42
|
+
super(message)
|
|
43
|
+
this.name = 'MohdelTypedError'
|
|
44
|
+
this.severity = severity
|
|
45
|
+
this.retryable = retryable
|
|
46
|
+
if (detail) this.detail = detail
|
|
47
|
+
if (type) this.type = type
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @returns {TypedError} */
|
|
51
|
+
toJSON () {
|
|
52
|
+
/** @type {TypedError} */
|
|
53
|
+
const out = {
|
|
54
|
+
message: this.message,
|
|
55
|
+
severity: this.severity,
|
|
56
|
+
retryable: this.retryable
|
|
57
|
+
}
|
|
58
|
+
if (this.detail) out.detail = this.detail
|
|
59
|
+
if (this.type) out.type = this.type
|
|
60
|
+
return out
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {TypedError} data
|
|
65
|
+
* @returns {MohdelTypedError}
|
|
66
|
+
*/
|
|
67
|
+
static fromJSON (data) {
|
|
68
|
+
return new MohdelTypedError(data.message, {
|
|
69
|
+
severity: data.severity,
|
|
70
|
+
retryable: data.retryable,
|
|
71
|
+
detail: data.detail,
|
|
72
|
+
type: data.type
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event union for the session → thin-gate → client stream.
|
|
3
|
+
*
|
|
4
|
+
* Three events:
|
|
5
|
+
* - `delta` — streaming chunk (message text or function-call args).
|
|
6
|
+
* - `done` — terminal with the full `AnswerResult`.
|
|
7
|
+
* - `error` — wire-format error (serializable `TypedError`).
|
|
8
|
+
*
|
|
9
|
+
* Rust mirror: `rust/thin-gate/src/protocol.rs::Event`.
|
|
10
|
+
*
|
|
11
|
+
* @module core/events
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {(DeltaEvent | DoneEvent | ErrorEvent)} Event
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Streaming chunk: `{ type: 'message' | 'function_call', delta: string }`.
|
|
20
|
+
*
|
|
21
|
+
* @typedef {object} DeltaEvent
|
|
22
|
+
* @property {'delta'} type
|
|
23
|
+
* @property {DeltaChunk} delta
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} DeltaChunk
|
|
28
|
+
* @property {('message'|'function_call')} type
|
|
29
|
+
* @property {string} delta
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Terminal event on success.
|
|
34
|
+
*
|
|
35
|
+
* @typedef {object} DoneEvent
|
|
36
|
+
* @property {'done'} type
|
|
37
|
+
* @property {AnswerResult} result
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Terminal event on failure.
|
|
42
|
+
*
|
|
43
|
+
* @typedef {object} ErrorEvent
|
|
44
|
+
* @property {'error'} type
|
|
45
|
+
* @property {import('./errors.js').TypedError} error
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Inference result — the shape returned by the factory `answer()` and
|
|
50
|
+
* carried in `DoneEvent.result`.
|
|
51
|
+
*
|
|
52
|
+
* @typedef {object} AnswerResult
|
|
53
|
+
* @property {import('./status.js').Status} status
|
|
54
|
+
* `'completed' | 'tool_use' | 'incomplete'`.
|
|
55
|
+
* @property {(string|null)} output
|
|
56
|
+
* Final text (null when `status === 'tool_use'` with no text).
|
|
57
|
+
* @property {number} inputTokens
|
|
58
|
+
* @property {number} outputTokens
|
|
59
|
+
* @property {number} thinkingTokens
|
|
60
|
+
* @property {number} cost
|
|
61
|
+
* USD, computed from curated pricing. Single number (not a breakdown).
|
|
62
|
+
* @property {Timestamps} timestamps
|
|
63
|
+
* @property {string} [warning]
|
|
64
|
+
* `'insufficientOutputBudget' | 'cancelled' | ...` additive union.
|
|
65
|
+
* @property {ToolCall[]} [toolCalls]
|
|
66
|
+
* Present when `status === 'tool_use'`.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `process.hrtime.bigint()` values as strings (nanoseconds).
|
|
71
|
+
*
|
|
72
|
+
* @typedef {object} Timestamps
|
|
73
|
+
* @property {string} start Adapter invocation start.
|
|
74
|
+
* @property {string} first Time of first delta.
|
|
75
|
+
* @property {string} end Completion time.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Tool call as emitted on `AnswerResult.toolCalls`. `arguments` is a
|
|
80
|
+
* **parsed object**, not a JSON string.
|
|
81
|
+
*
|
|
82
|
+
* @typedef {object} ToolCall
|
|
83
|
+
* @property {string} id
|
|
84
|
+
* @property {string} name
|
|
85
|
+
* @property {object} arguments
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
export const EVENT_TYPES = Object.freeze(['delta', 'done', 'error'])
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {unknown} x
|
|
92
|
+
* @returns {x is Event}
|
|
93
|
+
*/
|
|
94
|
+
export function isEvent (x) {
|
|
95
|
+
return x !== null && typeof x === 'object' && EVENT_TYPES.includes(/** @type {any} */(x).type)
|
|
96
|
+
}
|
package/js/core/image.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image-generation envelope and result.
|
|
3
|
+
*
|
|
4
|
+
* Separate call path from `CallEnvelope` / `AnswerResult`: image
|
|
5
|
+
* generation is a single synchronous request/response (no streaming).
|
|
6
|
+
* Result shape: `{ status, images, seed, timestamps }`.
|
|
7
|
+
*
|
|
8
|
+
* @module core/image
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {object} ImageEnvelope
|
|
13
|
+
*
|
|
14
|
+
* @property {string} callId
|
|
15
|
+
* @property {string} authId
|
|
16
|
+
* @property {import('./envelope.js').Auth} auth
|
|
17
|
+
* @property {string} [traceparent]
|
|
18
|
+
* @property {string} [baggage]
|
|
19
|
+
*
|
|
20
|
+
* @property {string} provider
|
|
21
|
+
* @property {string} model
|
|
22
|
+
* @property {string} prompt
|
|
23
|
+
*
|
|
24
|
+
* @property {string} [size] e.g. "1024x1024". Provider-specific.
|
|
25
|
+
* @property {number} [seed] Deterministic generation seed (provider support varies).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {object} ImageData
|
|
30
|
+
* @property {string} mimeType
|
|
31
|
+
* @property {string} [url] Remote URL (transient — providers may expire).
|
|
32
|
+
* @property {string} [base64] Inline base64-encoded image bytes.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {object} ImageResult
|
|
37
|
+
*
|
|
38
|
+
* @property {'completed'} status
|
|
39
|
+
* Images are one-shot — no `incomplete` state.
|
|
40
|
+
* @property {ImageData[]} images
|
|
41
|
+
* @property {number | null} seed
|
|
42
|
+
* Echo of provider seed when available; null otherwise.
|
|
43
|
+
* @property {{start: string, first: string, end: string}} timestamps
|
|
44
|
+
* hrtime-bigint-as-string. `first` = `end` for image (no streaming).
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
export const IMAGE_ENVELOPE_FIELDS = Object.freeze([
|
|
48
|
+
'callId',
|
|
49
|
+
'authId',
|
|
50
|
+
'auth',
|
|
51
|
+
'traceparent',
|
|
52
|
+
'baggage',
|
|
53
|
+
'provider',
|
|
54
|
+
'model',
|
|
55
|
+
'prompt',
|
|
56
|
+
'size',
|
|
57
|
+
'seed'
|
|
58
|
+
])
|
package/js/core/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Answer-result status contract.
|
|
3
|
+
*
|
|
4
|
+
* Three states:
|
|
5
|
+
* - `completed` — the call finished normally.
|
|
6
|
+
* - `tool_use` — the model emitted tool calls; the caller is
|
|
7
|
+
* expected to round-trip results back.
|
|
8
|
+
* - `incomplete` — the call was cut short (budget, cancel, policy).
|
|
9
|
+
*
|
|
10
|
+
* Rust mirror: `rust/thin-gate/src/protocol.rs::Status`.
|
|
11
|
+
*
|
|
12
|
+
* @module core/status
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** @typedef {('completed'|'tool_use'|'incomplete')} Status */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Warning values emitted on `AnswerResult.warning` when status is
|
|
19
|
+
* `incomplete`:
|
|
20
|
+
* - `insufficientOutputBudget` — the model hit `max_tokens`.
|
|
21
|
+
* - `cancelled` — the call was aborted via a cancel control
|
|
22
|
+
* message or `AbortSignal`.
|
|
23
|
+
*
|
|
24
|
+
* Additive: future releases may add new warning strings; consumers
|
|
25
|
+
* should treat unknown warnings as pass-through metadata.
|
|
26
|
+
*
|
|
27
|
+
* @typedef {('insufficientOutputBudget'|'cancelled'|string)} Warning
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export const STATUS_COMPLETED = 'completed'
|
|
31
|
+
export const STATUS_TOOL_USE = 'tool_use'
|
|
32
|
+
export const STATUS_INCOMPLETE = 'incomplete'
|
|
33
|
+
export const STATUSES = Object.freeze([
|
|
34
|
+
STATUS_COMPLETED,
|
|
35
|
+
STATUS_TOOL_USE,
|
|
36
|
+
STATUS_INCOMPLETE
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
export const WARNING_INSUFFICIENT_OUTPUT_BUDGET = 'insufficientOutputBudget'
|
|
40
|
+
export const WARNING_CANCELLED = 'cancelled'
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {unknown} x
|
|
44
|
+
* @returns {x is Status}
|
|
45
|
+
*/
|
|
46
|
+
export function isStatus (x) {
|
|
47
|
+
return typeof x === 'string' && STATUSES.includes(/** @type {Status} */(x))
|
|
48
|
+
}
|