@siduri-x/brain 2.0.3 → 2.0.4
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 +29 -4
- package/dist/index.test.js +25 -0
- package/organ-manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -156,6 +156,23 @@ class OpenAICompatibleBrain {
|
|
|
156
156
|
});
|
|
157
157
|
if (!response.ok) {
|
|
158
158
|
const status = response.status;
|
|
159
|
+
let errorDetails = response.statusText || `HTTP ${status}`;
|
|
160
|
+
try {
|
|
161
|
+
if (typeof response.text === 'function') {
|
|
162
|
+
const errorText = await response.text();
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(errorText);
|
|
165
|
+
errorDetails = parsed?.error?.message || parsed?.message || errorText;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
if (errorText)
|
|
169
|
+
errorDetails = errorText;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// fallback
|
|
175
|
+
}
|
|
159
176
|
const retryHeader = response.headers?.get ? response.headers.get('retry-after') : undefined;
|
|
160
177
|
if (retryHeader) {
|
|
161
178
|
const parsedSec = parseInt(retryHeader, 10);
|
|
@@ -163,11 +180,11 @@ class OpenAICompatibleBrain {
|
|
|
163
180
|
retryAfterSec = parsedSec;
|
|
164
181
|
}
|
|
165
182
|
}
|
|
166
|
-
// Client authentication, forbidden, and bad request errors are fatal and should not be retried
|
|
167
|
-
if (status === 400 || status === 401 || status === 403 || status === 404) {
|
|
168
|
-
throw new Error(`Fatal upstream API error (${status}): ${
|
|
183
|
+
// Client authentication, forbidden, insufficient credits, and bad request errors are fatal and should not be retried
|
|
184
|
+
if (status === 400 || status === 401 || status === 402 || status === 403 || status === 404) {
|
|
185
|
+
throw new Error(`Fatal upstream API error (${status}): ${errorDetails}`);
|
|
169
186
|
}
|
|
170
|
-
throw new Error(`OpenRouter API error: ${
|
|
187
|
+
throw new Error(`OpenRouter API error (${status}): ${errorDetails}`);
|
|
171
188
|
}
|
|
172
189
|
const data = await response.json();
|
|
173
190
|
const toolCall = data.choices?.[0]?.message?.tool_calls?.[0];
|
|
@@ -176,6 +193,14 @@ class OpenAICompatibleBrain {
|
|
|
176
193
|
const parsed = ResponsePlanSchema.parse(rawArgs);
|
|
177
194
|
return parsed;
|
|
178
195
|
}
|
|
196
|
+
const directContent = data.choices?.[0]?.message?.content;
|
|
197
|
+
if (typeof directContent === 'string' && directContent.trim().length > 0) {
|
|
198
|
+
return {
|
|
199
|
+
speech: directContent.trim(),
|
|
200
|
+
language: 'en',
|
|
201
|
+
internalMonologue: 'Direct completion without tool call',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
179
204
|
throw new Error("No valid tool call returned from OpenRouter");
|
|
180
205
|
}
|
|
181
206
|
catch (e) {
|
package/dist/index.test.js
CHANGED
|
@@ -119,6 +119,31 @@ describe('OpenRouterBrain', () => {
|
|
|
119
119
|
});
|
|
120
120
|
await expect(fastTimeoutBrain.generatePlan(mockContext)).rejects.toThrow(/deadline/i);
|
|
121
121
|
});
|
|
122
|
+
test('extracts detailed provider error message from upstream JSON body', async () => {
|
|
123
|
+
global.fetch.mockResolvedValue({
|
|
124
|
+
ok: false,
|
|
125
|
+
status: 402,
|
|
126
|
+
statusText: "Payment Required",
|
|
127
|
+
text: async () => JSON.stringify({ error: { message: "Provider balance exhausted. Please top up your account." } }),
|
|
128
|
+
});
|
|
129
|
+
await expect(brain.generatePlan(mockContext)).rejects.toThrow("Provider balance exhausted");
|
|
130
|
+
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
131
|
+
});
|
|
132
|
+
test('falls back gracefully to direct message content when model omits tool_calls', async () => {
|
|
133
|
+
global.fetch.mockResolvedValueOnce({
|
|
134
|
+
ok: true,
|
|
135
|
+
json: async () => ({
|
|
136
|
+
choices: [{
|
|
137
|
+
message: {
|
|
138
|
+
content: "Hello from direct completion!",
|
|
139
|
+
},
|
|
140
|
+
}],
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
const plan = await brain.generatePlan(mockContext);
|
|
144
|
+
expect(plan.speech).toBe("Hello from direct completion!");
|
|
145
|
+
expect(plan.language).toBe("en");
|
|
146
|
+
});
|
|
122
147
|
});
|
|
123
148
|
describe('OpenAICompatibleBrain', () => {
|
|
124
149
|
test('uses a configurable OpenAI-compatible endpoint', async () => {
|
package/organ-manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siduri-x/brain",
|
|
3
3
|
"organType": "brain",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.4",
|
|
5
5
|
"displayName": "Brain (Cognition & Planning)",
|
|
6
6
|
"description": "Provider-neutral LLM reasoning, response planning, and proposal generation",
|
|
7
7
|
"entrypoint": "./dist/index.js",
|