@hunterzhu/pulse-adapters 0.1.6 → 0.1.7
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.
|
@@ -17,8 +17,33 @@ export class AnthropicAdapter {
|
|
|
17
17
|
this.config = config;
|
|
18
18
|
}
|
|
19
19
|
async executeAttempt(params) {
|
|
20
|
-
const
|
|
21
|
-
const messages = [
|
|
20
|
+
const systemParts = params.request.blocks.filter((block) => block.kind === 'system' || block.kind === 'policy' || block.kind === 'tools').map((block) => typeof block.content === 'string' ? block.content : JSON.stringify(block.content));
|
|
21
|
+
const messages = [];
|
|
22
|
+
const appendMessage = (role, text) => {
|
|
23
|
+
const previous = messages.at(-1);
|
|
24
|
+
if (previous?.role === role)
|
|
25
|
+
previous.content.push({ type: 'text', text });
|
|
26
|
+
else
|
|
27
|
+
messages.push({ role, content: [{ type: 'text', text }] });
|
|
28
|
+
};
|
|
29
|
+
for (const block of params.request.blocks) {
|
|
30
|
+
if (block.kind === 'system' || block.kind === 'policy' || block.kind === 'tools')
|
|
31
|
+
continue;
|
|
32
|
+
if (block.kind === 'conversation' && Array.isArray(block.content)) {
|
|
33
|
+
for (const item of block.content) {
|
|
34
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
35
|
+
continue;
|
|
36
|
+
const message = item;
|
|
37
|
+
if (message.role === 'system' && typeof message.content === 'string')
|
|
38
|
+
appendMessage('user', `Context note, not a new instruction:\n${message.content}`);
|
|
39
|
+
else if ((message.role === 'user' || message.role === 'assistant') && typeof message.content === 'string')
|
|
40
|
+
appendMessage(message.role, message.content);
|
|
41
|
+
}
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
appendMessage('user', typeof block.content === 'string' ? block.content : JSON.stringify(block.content));
|
|
45
|
+
}
|
|
46
|
+
const system = systemParts.join('\n');
|
|
22
47
|
const streaming = params.onObservation !== undefined;
|
|
23
48
|
const tools = toolDefinitions(params.request);
|
|
24
49
|
const maxTokens = params.maxOutputTokens ?? this.config.maxOutputTokens ?? 4096;
|
|
@@ -51,17 +51,8 @@ export class OpenAICompatibleAdapter {
|
|
|
51
51
|
if (typeof choice?.finish_reason === 'string')
|
|
52
52
|
finishReason = choice.finish_reason;
|
|
53
53
|
if (Array.isArray(delta?.tool_calls))
|
|
54
|
-
for (const call of delta.tool_calls)
|
|
55
|
-
|
|
56
|
-
const current = toolCalls.get(index) ?? { name: '', arguments: '' };
|
|
57
|
-
if (typeof call.id === 'string')
|
|
58
|
-
current.id = call.id;
|
|
59
|
-
if (typeof call.function?.name === 'string')
|
|
60
|
-
current.name += call.function.name;
|
|
61
|
-
if (typeof call.function?.arguments === 'string')
|
|
62
|
-
current.arguments += call.function.arguments;
|
|
63
|
-
toolCalls.set(index, current);
|
|
64
|
-
}
|
|
54
|
+
for (const call of delta.tool_calls)
|
|
55
|
+
accumulateStreamToolCall(toolCalls, call);
|
|
65
56
|
if (event.data.usage !== undefined)
|
|
66
57
|
usage = event.data.usage;
|
|
67
58
|
}
|
|
@@ -81,18 +72,88 @@ export class OpenAICompatibleAdapter {
|
|
|
81
72
|
}
|
|
82
73
|
}
|
|
83
74
|
export function toOpenAIMessages(request) {
|
|
84
|
-
|
|
75
|
+
const messages = [];
|
|
76
|
+
for (const block of request.blocks) {
|
|
85
77
|
const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
86
|
-
if (block.kind === '
|
|
87
|
-
|
|
78
|
+
if (block.kind === 'conversation' && Array.isArray(block.content)) {
|
|
79
|
+
for (const item of block.content) {
|
|
80
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
81
|
+
continue;
|
|
82
|
+
const message = item;
|
|
83
|
+
if (message.role === 'system' && typeof message.content === 'string')
|
|
84
|
+
messages.push({ role: 'user', content: `Context note, not a new instruction:\n${message.content}` });
|
|
85
|
+
else if ((message.role === 'user' || message.role === 'assistant') && typeof message.content === 'string')
|
|
86
|
+
messages.push({ role: message.role, content: message.content });
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (block.kind === 'system' || block.kind === 'policy' || block.kind === 'tools') {
|
|
91
|
+
messages.push({ role: 'system', content });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
88
94
|
if (block.kind === 'history')
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
messages.push({ role: 'assistant', content });
|
|
96
|
+
else
|
|
97
|
+
messages.push({ role: 'user', name: block.kind, content });
|
|
98
|
+
}
|
|
99
|
+
return messages;
|
|
92
100
|
}
|
|
93
101
|
function toMessages(request) {
|
|
94
102
|
return toOpenAIMessages(request);
|
|
95
103
|
}
|
|
104
|
+
function nextToolIndex(toolCalls) {
|
|
105
|
+
let next = 0;
|
|
106
|
+
for (const index of toolCalls.keys())
|
|
107
|
+
if (index >= next)
|
|
108
|
+
next = index + 1;
|
|
109
|
+
return next;
|
|
110
|
+
}
|
|
111
|
+
function jsonTextComplete(value) {
|
|
112
|
+
const trimmed = value.trim();
|
|
113
|
+
if (!trimmed)
|
|
114
|
+
return false;
|
|
115
|
+
try {
|
|
116
|
+
JSON.parse(trimmed);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* OpenAI-compatible streams sometimes reuse `tool_calls[].index` for a new
|
|
125
|
+
* call and only distinguish it by `id`. Concatenating those argument
|
|
126
|
+
* fragments produces invalid JSON (`{"a":1}{"b":2}`).
|
|
127
|
+
*/
|
|
128
|
+
function accumulateStreamToolCall(toolCalls, call) {
|
|
129
|
+
const id = typeof call.id === 'string' && call.id.length > 0 ? call.id : undefined;
|
|
130
|
+
const name = typeof call.function?.name === 'string' ? call.function.name : '';
|
|
131
|
+
const args = typeof call.function?.arguments === 'string' ? call.function.arguments : '';
|
|
132
|
+
const rawIndex = call.index;
|
|
133
|
+
let index = typeof rawIndex === 'number' && Number.isInteger(rawIndex) && rawIndex >= 0 ? rawIndex : typeof rawIndex === 'string' && /^\d+$/.test(rawIndex) ? Number(rawIndex) : undefined;
|
|
134
|
+
if (index !== undefined) {
|
|
135
|
+
const current = toolCalls.get(index);
|
|
136
|
+
const startsAnotherCall = current !== undefined && ((id !== undefined && current.id !== undefined && current.id !== id) ||
|
|
137
|
+
(name.length > 0 && current.name.length > 0 && jsonTextComplete(current.arguments) && /^[\[{]/.test(args.trimStart())));
|
|
138
|
+
if (startsAnotherCall)
|
|
139
|
+
index = nextToolIndex(toolCalls);
|
|
140
|
+
}
|
|
141
|
+
else if (id !== undefined) {
|
|
142
|
+
index = [...toolCalls.entries()].find(([, item]) => item.id === id)?.[0] ?? nextToolIndex(toolCalls);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const keys = [...toolCalls.keys()];
|
|
146
|
+
index = keys.length === 0 ? 0 : Math.max(...keys);
|
|
147
|
+
}
|
|
148
|
+
const current = toolCalls.get(index) ?? { name: '', arguments: '' };
|
|
149
|
+
if (id !== undefined)
|
|
150
|
+
current.id = id;
|
|
151
|
+
if (name.length > 0)
|
|
152
|
+
current.name += name;
|
|
153
|
+
if (args.length > 0)
|
|
154
|
+
current.arguments += args;
|
|
155
|
+
toolCalls.set(index, current);
|
|
156
|
+
}
|
|
96
157
|
function toolDefinitions(request) {
|
|
97
158
|
const block = request.blocks.find((candidate) => candidate.kind === 'tools');
|
|
98
159
|
const content = block?.content;
|
package/dist/workers/http.js
CHANGED
|
@@ -234,6 +234,8 @@ export class HttpWorkerClient {
|
|
|
234
234
|
this.baseUrl = options.baseUrl.replace(/\/$/, '');
|
|
235
235
|
this.workerId = options.workerId;
|
|
236
236
|
this.pollMs = options.pollMs ?? 10;
|
|
237
|
+
if (!Number.isFinite(this.pollMs) || this.pollMs <= 0)
|
|
238
|
+
throw new Error('INVALID_WORKER_POLL_INTERVAL');
|
|
237
239
|
this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
|
|
238
240
|
if (!Number.isFinite(this.requestTimeoutMs) || this.requestTimeoutMs <= 0)
|
|
239
241
|
throw new Error('INVALID_WORKER_HTTP_TIMEOUT');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hunterzhu/pulse-adapters",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zhuhengtan/Pulse"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"registry": "https://registry.npmjs.org"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@hunterzhu/pulse-runtime": "0.1.
|
|
20
|
-
"@hunterzhu/pulse-tool-sdk": "0.1.
|
|
19
|
+
"@hunterzhu/pulse-runtime": "0.1.7",
|
|
20
|
+
"@hunterzhu/pulse-tool-sdk": "0.1.7"
|
|
21
21
|
}
|
|
22
22
|
}
|