@tangle-network/agent-provider-cli-bridge 0.2.25 → 0.2.27
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/dist/index.js +100 -20
- package/package.json +15 -24
package/dist/index.js
CHANGED
|
@@ -40,7 +40,10 @@ async function* streamCliBridgeTurn(fetchImpl, options, environmentInput, turn)
|
|
|
40
40
|
if (!response.body)
|
|
41
41
|
throw new Error("cli-bridge response body is empty");
|
|
42
42
|
let text = "";
|
|
43
|
-
|
|
43
|
+
const sessionId = turn.sessionId ?? environmentInput.idempotencyKey ?? environmentInput.name ?? "cli-bridge";
|
|
44
|
+
const messageId = turn.turnId ?? `${sessionId}:assistant`;
|
|
45
|
+
const emittedToolCalls = new Set();
|
|
46
|
+
let completed = false;
|
|
44
47
|
for await (const event of parseSse(response.body)) {
|
|
45
48
|
if (event === "[DONE]")
|
|
46
49
|
break;
|
|
@@ -51,35 +54,80 @@ async function* streamCliBridgeTurn(fetchImpl, options, environmentInput, turn)
|
|
|
51
54
|
const error = parsed.error;
|
|
52
55
|
const message = typeof error.message === "string" ? error.message : "cli-bridge error";
|
|
53
56
|
yield { type: "status", data: { status: "failed", error: message } };
|
|
54
|
-
|
|
57
|
+
throw new Error(`cli-bridge: ${message}`);
|
|
55
58
|
}
|
|
56
59
|
const choice = Array.isArray(parsed.choices) ? parsed.choices[0] : undefined;
|
|
57
60
|
const delta = choice?.delta;
|
|
58
61
|
const chunk = delta && typeof delta.content === "string" ? delta.content : "";
|
|
59
62
|
const nextUsage = usageFromOpenAi(parsed.usage);
|
|
60
63
|
if (nextUsage) {
|
|
61
|
-
|
|
62
|
-
yield { type: "usage", data: { usage: nextUsage }, usage: nextUsage };
|
|
64
|
+
yield { type: "usage", data: {}, usage: nextUsage };
|
|
63
65
|
}
|
|
64
66
|
if (chunk) {
|
|
65
67
|
text += chunk;
|
|
68
|
+
const part = {
|
|
69
|
+
id: `${messageId}:text`,
|
|
70
|
+
sessionID: sessionId,
|
|
71
|
+
messageID: messageId,
|
|
72
|
+
type: "text",
|
|
73
|
+
text,
|
|
74
|
+
};
|
|
75
|
+
const normalized = {
|
|
76
|
+
type: "message.part.updated",
|
|
77
|
+
part,
|
|
78
|
+
delta: chunk,
|
|
79
|
+
};
|
|
66
80
|
yield {
|
|
67
81
|
type: "message.part.updated",
|
|
68
|
-
data: { delta: chunk },
|
|
82
|
+
data: { part, delta: chunk },
|
|
83
|
+
normalized,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
for (const toolCall of toolCallsFromDelta(delta)) {
|
|
87
|
+
const callId = toolCall.id ?? `${messageId}:tool:${toolCall.index}`;
|
|
88
|
+
if (!toolCall.name || emittedToolCalls.has(callId))
|
|
89
|
+
continue;
|
|
90
|
+
emittedToolCalls.add(callId);
|
|
91
|
+
const part = {
|
|
92
|
+
id: callId,
|
|
93
|
+
sessionID: sessionId,
|
|
94
|
+
messageID: messageId,
|
|
95
|
+
type: "tool",
|
|
96
|
+
callID: callId,
|
|
97
|
+
tool: toolCall.name,
|
|
98
|
+
state: { status: "pending", input: {} },
|
|
99
|
+
};
|
|
100
|
+
const normalized = {
|
|
101
|
+
type: "message.part.updated",
|
|
102
|
+
part,
|
|
103
|
+
};
|
|
104
|
+
yield {
|
|
105
|
+
type: "message.part.updated",
|
|
106
|
+
data: { part },
|
|
107
|
+
normalized,
|
|
69
108
|
};
|
|
70
109
|
}
|
|
71
110
|
if (choice?.finish_reason) {
|
|
111
|
+
if (choice.finish_reason === "error") {
|
|
112
|
+
yield {
|
|
113
|
+
type: "status",
|
|
114
|
+
data: { status: "failed", error: "cli-bridge returned finish_reason=error" },
|
|
115
|
+
};
|
|
116
|
+
throw new Error("cli-bridge returned finish_reason=error");
|
|
117
|
+
}
|
|
118
|
+
completed = true;
|
|
72
119
|
yield {
|
|
73
120
|
type: "result",
|
|
74
121
|
data: {
|
|
75
122
|
finalText: text,
|
|
76
123
|
finishReason: choice.finish_reason,
|
|
77
|
-
status:
|
|
124
|
+
status: "completed",
|
|
78
125
|
},
|
|
79
|
-
...(usage ? { usage } : {}),
|
|
80
126
|
};
|
|
81
127
|
}
|
|
82
128
|
}
|
|
129
|
+
if (!completed)
|
|
130
|
+
throw new Error("cli-bridge stream ended without a terminal result");
|
|
83
131
|
}
|
|
84
132
|
function toChatCompletionsBody(options, environmentInput, turn) {
|
|
85
133
|
const profile = inlineProfile(environmentInput.profile);
|
|
@@ -137,14 +185,14 @@ async function* parseSse(body) {
|
|
|
137
185
|
if (done)
|
|
138
186
|
break;
|
|
139
187
|
buffer += decoder.decode(value, { stream: true });
|
|
140
|
-
let boundary = buffer
|
|
141
|
-
while (boundary
|
|
142
|
-
const frame = buffer.slice(0, boundary);
|
|
143
|
-
buffer = buffer.slice(boundary +
|
|
188
|
+
let boundary = findFrameBoundary(buffer);
|
|
189
|
+
while (boundary) {
|
|
190
|
+
const frame = buffer.slice(0, boundary.index);
|
|
191
|
+
buffer = buffer.slice(boundary.index + boundary.length);
|
|
144
192
|
const data = dataFromFrame(frame);
|
|
145
193
|
if (data !== undefined)
|
|
146
194
|
yield data;
|
|
147
|
-
boundary = buffer
|
|
195
|
+
boundary = findFrameBoundary(buffer);
|
|
148
196
|
}
|
|
149
197
|
}
|
|
150
198
|
if (buffer) {
|
|
@@ -157,6 +205,15 @@ async function* parseSse(body) {
|
|
|
157
205
|
reader.releaseLock();
|
|
158
206
|
}
|
|
159
207
|
}
|
|
208
|
+
function findFrameBoundary(value) {
|
|
209
|
+
const lf = value.indexOf("\n\n");
|
|
210
|
+
const crlf = value.indexOf("\r\n\r\n");
|
|
211
|
+
if (lf === -1 && crlf === -1)
|
|
212
|
+
return undefined;
|
|
213
|
+
if (crlf !== -1 && (lf === -1 || crlf < lf))
|
|
214
|
+
return { index: crlf, length: 4 };
|
|
215
|
+
return { index: lf, length: 2 };
|
|
216
|
+
}
|
|
160
217
|
function dataFromFrame(frame) {
|
|
161
218
|
const lines = frame.split(/\r?\n/);
|
|
162
219
|
const data = lines
|
|
@@ -165,6 +222,25 @@ function dataFromFrame(frame) {
|
|
|
165
222
|
.join("\n");
|
|
166
223
|
return data || undefined;
|
|
167
224
|
}
|
|
225
|
+
function toolCallsFromDelta(value) {
|
|
226
|
+
if (!value || typeof value !== "object")
|
|
227
|
+
return [];
|
|
228
|
+
const calls = value.tool_calls;
|
|
229
|
+
if (!Array.isArray(calls))
|
|
230
|
+
return [];
|
|
231
|
+
return calls.flatMap((call, position) => {
|
|
232
|
+
if (!call || typeof call !== "object")
|
|
233
|
+
return [];
|
|
234
|
+
const record = call;
|
|
235
|
+
const fn = record.function;
|
|
236
|
+
const name = fn && typeof fn === "object" && typeof fn.name === "string"
|
|
237
|
+
? fn.name
|
|
238
|
+
: undefined;
|
|
239
|
+
const index = number(record.index) ?? position;
|
|
240
|
+
const id = typeof record.id === "string" ? record.id : undefined;
|
|
241
|
+
return [{ ...(id ? { id } : {}), index, ...(name ? { name } : {}) }];
|
|
242
|
+
});
|
|
243
|
+
}
|
|
168
244
|
function safeJson(value) {
|
|
169
245
|
try {
|
|
170
246
|
const parsed = JSON.parse(value);
|
|
@@ -182,15 +258,19 @@ function usageFromOpenAi(value) {
|
|
|
182
258
|
const outputTokens = number(record.completion_tokens) ?? number(record.output_tokens);
|
|
183
259
|
if (inputTokens === undefined && outputTokens === undefined)
|
|
184
260
|
return undefined;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
261
|
+
const totalTokens = number(record.total_tokens);
|
|
262
|
+
const cacheReadInputTokens = number(record.cache_read_input_tokens);
|
|
263
|
+
const cacheCreationInputTokens = number(record.cache_creation_input_tokens);
|
|
264
|
+
const reasoningTokens = number(record.reasoning_tokens);
|
|
265
|
+
const cost = number(record.cost);
|
|
190
266
|
return {
|
|
191
|
-
inputTokens:
|
|
192
|
-
outputTokens:
|
|
193
|
-
...(
|
|
267
|
+
inputTokens: inputTokens ?? 0,
|
|
268
|
+
outputTokens: outputTokens ?? 0,
|
|
269
|
+
...(totalTokens !== undefined ? { totalTokens } : {}),
|
|
270
|
+
...(cacheReadInputTokens !== undefined ? { cacheReadInputTokens } : {}),
|
|
271
|
+
...(cacheCreationInputTokens !== undefined ? { cacheCreationInputTokens } : {}),
|
|
272
|
+
...(reasoningTokens !== undefined ? { reasoningTokens } : {}),
|
|
273
|
+
...(cost !== undefined ? { cost } : {}),
|
|
194
274
|
};
|
|
195
275
|
}
|
|
196
276
|
function number(value) {
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-provider-cli-bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.27",
|
|
4
4
|
"description": "AgentEnvironmentProvider adapter for cli-bridge /v1/chat/completions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"types": "
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"import": "./dist/index.js",
|
|
12
|
-
"types": "./
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
13
14
|
}
|
|
14
15
|
},
|
|
15
16
|
"repository": {
|
|
@@ -19,16 +20,7 @@
|
|
|
19
20
|
},
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public",
|
|
22
|
-
"registry": "https://registry.npmjs.org"
|
|
23
|
-
"main": "./dist/index.js",
|
|
24
|
-
"types": "./dist/index.d.ts",
|
|
25
|
-
"exports": {
|
|
26
|
-
".": {
|
|
27
|
-
"import": "./dist/index.js",
|
|
28
|
-
"types": "./dist/index.d.ts",
|
|
29
|
-
"default": "./dist/index.js"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
23
|
+
"registry": "https://registry.npmjs.org"
|
|
32
24
|
},
|
|
33
25
|
"files": [
|
|
34
26
|
"dist/index.d.ts",
|
|
@@ -36,19 +28,18 @@
|
|
|
36
28
|
"README.md",
|
|
37
29
|
"LICENSE"
|
|
38
30
|
],
|
|
39
|
-
"scripts": {
|
|
40
|
-
"build": "tsc -p tsconfig.json",
|
|
41
|
-
"check-types": "tsc --noEmit",
|
|
42
|
-
"clean": "rm -rf dist",
|
|
43
|
-
"prepublishOnly": "pnpm run build",
|
|
44
|
-
"test": "vitest run"
|
|
45
|
-
},
|
|
46
31
|
"dependencies": {
|
|
47
32
|
"@tangle-network/agent-interface": "0.34.0"
|
|
48
33
|
},
|
|
49
34
|
"devDependencies": {
|
|
50
|
-
"@types/node": "
|
|
35
|
+
"@types/node": "25.6.0",
|
|
51
36
|
"typescript": "^6.0.3",
|
|
52
|
-
"vitest": "
|
|
37
|
+
"vitest": "^4.1.5"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc -p tsconfig.json",
|
|
41
|
+
"check-types": "tsc --noEmit",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"test": "vitest run"
|
|
53
44
|
}
|
|
54
|
-
}
|
|
45
|
+
}
|