@startsimpli/llm 0.4.20 → 0.4.21
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/package.json
CHANGED
|
@@ -144,6 +144,21 @@ describe('adapting to what a model refuses', () => {
|
|
|
144
144
|
expect(sent).toHaveLength(3);
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
it('parallel first calls all succeed: a lesson learned by one request applies to the others in flight', async () => {
|
|
148
|
+
// Regression: crochet's assembly pipeline fires its connection calls in
|
|
149
|
+
// parallel. All of them sent 'none' before any answer; the first 400 taught
|
|
150
|
+
// the provider 'low', and the others saw that lesson as already learned and
|
|
151
|
+
// threw instead of retrying (gpt-6-astra: 12 of 12 connections failed).
|
|
152
|
+
const p = provider('gpt-6.1-sol');
|
|
153
|
+
const effortErr = { code: 'unsupported_value', param: 'reasoning_effort', message: "Unsupported value: 'reasoning_effort' does not support 'none' with this model. Supported values are: 'low', 'medium', 'high', and 'xhigh'." };
|
|
154
|
+
reject(effortErr);
|
|
155
|
+
reject(effortErr);
|
|
156
|
+
reject(effortErr);
|
|
157
|
+
const results = await Promise.all([1, 2, 3].map(() => p.generate('u', 's', { reasoningEffort: 'none' })));
|
|
158
|
+
expect(results.every((r) => r.content === '{"ok":true}')).toBe(true);
|
|
159
|
+
expect(sent.slice(3).every((b) => b.reasoning_effort === 'low')).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
147
162
|
it('adapts on the tool-calling path too', async () => {
|
|
148
163
|
const p = provider('gpt-6-sol');
|
|
149
164
|
reject({ code: 'unsupported_value', param: 'temperature', message: "Unsupported value: 'temperature' does not support 0.2 with this model. Only the default (1) value is supported." });
|
package/src/providers/openai.ts
CHANGED
|
@@ -270,20 +270,27 @@ export class OpenAIProvider implements ILLMProvider {
|
|
|
270
270
|
): Promise<T> {
|
|
271
271
|
const q = this.quirksFor(model);
|
|
272
272
|
for (let attempt = 0; ; attempt++) {
|
|
273
|
+
const body = JSON.stringify(build(q));
|
|
273
274
|
const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
|
|
274
275
|
method: 'POST',
|
|
275
276
|
headers: {
|
|
276
277
|
'Content-Type': 'application/json',
|
|
277
278
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
278
279
|
},
|
|
279
|
-
body
|
|
280
|
+
body,
|
|
280
281
|
signal,
|
|
281
282
|
});
|
|
282
283
|
if (response.ok) return (await response.json()) as T;
|
|
283
284
|
const errorData = (await response.json().catch(() => ({
|
|
284
285
|
error: { message: 'Unknown error' },
|
|
285
286
|
}))) as OpenAIErrorResponse;
|
|
286
|
-
if (response.status === 400 && attempt < MAX_ADAPTIVE_RETRIES
|
|
287
|
+
if (response.status === 400 && attempt < MAX_ADAPTIVE_RETRIES) {
|
|
288
|
+
const learned = learnFromError(q, errorData.error, sent);
|
|
289
|
+
// Calls to a new model often go out in parallel: by the time this one
|
|
290
|
+
// fails, another may already have learned the lesson, so retry whenever
|
|
291
|
+
// the request would now be built differently from the one just sent.
|
|
292
|
+
if (learned || JSON.stringify(build(q)) !== body) continue;
|
|
293
|
+
}
|
|
287
294
|
throw this.handleApiError(response.status, errorData);
|
|
288
295
|
}
|
|
289
296
|
}
|