dsh-bailinghub 0.6.0 → 0.7.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/CHANGELOG.md +5 -0
- package/README.md +6 -6
- package/docs/COMPATIBILITY.md +7 -7
- package/docs/README.zh-CN.md +6 -6
- package/docs/RELEASE_NOTES_v0.7.0.en.md +15 -0
- package/docs/RELEASE_NOTES_v0.7.0.md +15 -0
- package/docs/UPGRADE_v0.7.0.en.md +15 -0
- package/docs/UPGRADE_v0.7.0.md +15 -0
- package/docs/USAGE_SERVICE.md +96 -0
- package/lib/index.js +9 -0
- package/lib/invocation-journal.js +5 -0
- package/lib/model-gateway-transport.js +246 -0
- package/lib/runtime.js +24 -0
- package/lib/session-usage-store.js +280 -0
- package/lib/usage-llm-stream.js +157 -0
- package/lib/usage-model-transport.js +206 -0
- package/lib/usage-provider-stream.js +82 -0
- package/package.json +9 -3
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const envelope = value => value?.schema === 'bailing.provider-response.v1'
|
|
2
|
+
const failure = code => Object.assign(new Error(`Model response unavailable (${code}).`), { code })
|
|
3
|
+
|
|
4
|
+
/** Feed a local provider adapter without replacing global fetch. The receipt pump is independent
|
|
5
|
+
* of the body reader: adapters may stop at [DONE], but original-result persistence must finish.
|
|
6
|
+
*/
|
|
7
|
+
export async function* usageNativeModelChunks(events, consumeResponse) {
|
|
8
|
+
let openResponse, failResponse, resolveReceipt, rejectReceipt, controller, cancelled = false
|
|
9
|
+
let header, body = '', bytes = 0, ended = false
|
|
10
|
+
const responseReady = new Promise((resolve, reject) => { openResponse = resolve; failResponse = reject })
|
|
11
|
+
const receiptReady = new Promise((resolve, reject) => { resolveReceipt = resolve; rejectReceipt = reject })
|
|
12
|
+
// The pump can reject before either consumer reaches its await.
|
|
13
|
+
responseReady.catch(() => {}); receiptReady.catch(() => {})
|
|
14
|
+
const encoder = new TextEncoder()
|
|
15
|
+
function open(value) {
|
|
16
|
+
if (!envelope(value) || !['sse', 'json'].includes(value.format)
|
|
17
|
+
|| !Number.isInteger(value.status) || value.status < 200 || value.status > 599
|
|
18
|
+
|| value.content_type !== (value.format === 'sse' ? 'text/event-stream' : 'application/json')) throw failure('USAGE_STREAM_INCOMPLETE')
|
|
19
|
+
if (header) {
|
|
20
|
+
if (['format', 'status', 'content_type'].some(key => header[key] !== value[key])) throw failure('USAGE_STREAM_INCOMPLETE')
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
header = value
|
|
24
|
+
const stream = new ReadableStream({ start(value) { controller = value }, cancel() { cancelled = true } })
|
|
25
|
+
// Fetch forbids a body for these status codes. The saved body remains available to the receipt check.
|
|
26
|
+
openResponse(new Response([204, 205, 304].includes(value.status) ? null : stream,
|
|
27
|
+
{ status: value.status, headers: { 'content-type': value.content_type } }))
|
|
28
|
+
}
|
|
29
|
+
function append(text) {
|
|
30
|
+
if (typeof text !== 'string') throw failure('USAGE_STREAM_INCOMPLETE')
|
|
31
|
+
const chunk = encoder.encode(text); bytes += chunk.byteLength
|
|
32
|
+
if (bytes > 4 * 1024 * 1024) throw failure('USAGE_PROVIDER_RESPONSE_TOO_LARGE')
|
|
33
|
+
body += text
|
|
34
|
+
if (!cancelled) controller.enqueue(chunk)
|
|
35
|
+
}
|
|
36
|
+
const pump = (async () => {
|
|
37
|
+
try {
|
|
38
|
+
for await (const event of events) {
|
|
39
|
+
if (ended) throw failure('USAGE_STREAM_INCOMPLETE')
|
|
40
|
+
if (event.type === 'provider') { open(event.provider); append(event.provider.data) }
|
|
41
|
+
if (event.type !== 'operation') continue
|
|
42
|
+
const receipt = event.operation, saved = receipt?.response
|
|
43
|
+
if (receipt?.result_state === 'failed' && receipt.delivery_state !== 'cancelled_late_result') throw Object.assign(new Error(receipt.error?.message ?? 'Model service rejected the request.'), {code: 'USAGE_PROVIDER_REJECTED', operationId: receipt.operation_id, feedback: {schema:'bailing.usage-feedback.v1',code:'USAGE_PROVIDER_REJECTED',dispatch:'rejected',next_action:'contact_operator',retryable:false}})
|
|
44
|
+
if (receipt?.delivery_state !== 'response_ready' || receipt.host_turn_active !== true) throw failure(
|
|
45
|
+
receipt?.delivery_state === 'cancelled_late_result' ? 'USAGE_CANCELLED' : 'USAGE_ORIGINAL_PENDING')
|
|
46
|
+
const hadPreview = Boolean(header)
|
|
47
|
+
open(saved)
|
|
48
|
+
if (typeof saved.body !== 'string') throw failure('USAGE_STREAM_INCOMPLETE')
|
|
49
|
+
if (!hadPreview) append(saved.body) // Recovery reads the recorded body once, never reissues a request.
|
|
50
|
+
if (body !== saved.body) throw failure('USAGE_STREAM_INCOMPLETE')
|
|
51
|
+
ended = true
|
|
52
|
+
if (!cancelled) controller.close()
|
|
53
|
+
resolveReceipt(receipt)
|
|
54
|
+
}
|
|
55
|
+
if (!ended) throw failure('USAGE_STREAM_INCOMPLETE')
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (controller && !cancelled) { try { controller.error(error) } catch {} }
|
|
58
|
+
failResponse(error); rejectReceipt(error)
|
|
59
|
+
throw error
|
|
60
|
+
}
|
|
61
|
+
})()
|
|
62
|
+
pump.catch(() => {})
|
|
63
|
+
try {
|
|
64
|
+
const response = await responseReady
|
|
65
|
+
for await (const chunk of consumeResponse(response)) {
|
|
66
|
+
// No executable tool block or terminal marker before the original receipt is durable locally.
|
|
67
|
+
if (chunk.type === 'finish' || chunk.type === 'tool-call-delta'
|
|
68
|
+
|| (chunk.type === 'block-start' && chunk.blockType === 'tool-call')
|
|
69
|
+
|| (chunk.type === 'block-end' && chunk.block?.type === 'tool-call')) await receiptReady
|
|
70
|
+
yield chunk
|
|
71
|
+
}
|
|
72
|
+
// Some native parsers return without a finish marker; AgentLoop must still not advance early.
|
|
73
|
+
await receiptReady
|
|
74
|
+
await pump
|
|
75
|
+
} finally {
|
|
76
|
+
// Drain the original operation even when a parser throws, returns early or cancels its reader.
|
|
77
|
+
// No retry/model call is performed by this adapter.
|
|
78
|
+
cancelled = true
|
|
79
|
+
// A late local persistence/identity failure keeps priority over a parser error.
|
|
80
|
+
await pump
|
|
81
|
+
}
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-bailinghub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Let a local DeepSeek Harness Agent use BailingHub to query data, update records, and run permitted actions in your online store, SaaS, or other admin system.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -58,7 +58,12 @@
|
|
|
58
58
|
"docs/RELEASE_NOTES_v0.6.0.md",
|
|
59
59
|
"docs/UPGRADE_v0.6.0.md",
|
|
60
60
|
"docs/RELEASE_NOTES_v0.6.0.en.md",
|
|
61
|
-
"docs/UPGRADE_v0.6.0.en.md"
|
|
61
|
+
"docs/UPGRADE_v0.6.0.en.md",
|
|
62
|
+
"docs/USAGE_SERVICE.md",
|
|
63
|
+
"docs/RELEASE_NOTES_v0.7.0.md",
|
|
64
|
+
"docs/RELEASE_NOTES_v0.7.0.en.md",
|
|
65
|
+
"docs/UPGRADE_v0.7.0.md",
|
|
66
|
+
"docs/UPGRADE_v0.7.0.en.md"
|
|
62
67
|
],
|
|
63
68
|
"engines": {
|
|
64
69
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -73,10 +78,11 @@
|
|
|
73
78
|
},
|
|
74
79
|
"dependencies": {
|
|
75
80
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
76
|
-
"bailinghub-mcp-server": "0.
|
|
81
|
+
"bailinghub-mcp-server": "0.7.0"
|
|
77
82
|
},
|
|
78
83
|
"devDependencies": {
|
|
79
84
|
"@deepseek-ai/cordis": "4.0.1",
|
|
85
|
+
"@deepseek-ai/dsh-agent-loop": "0.1.1-rc.2",
|
|
80
86
|
"@deepseek-ai/dsh-commands": "0.1.1-rc.2",
|
|
81
87
|
"@deepseek-ai/dsh-scope": "0.1.1-rc.2",
|
|
82
88
|
"@deepseek-ai/dsh-system-prompt": "0.1.1-rc.2",
|