onbuzz 6.3.1 → 6.3.3

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onbuzz",
3
- "version": "6.3.1",
3
+ "version": "6.3.3",
4
4
  "description": "Loxia OnBuzz - Your AI Fleet",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -252,11 +252,37 @@ describe('param-quirk ladder (backend path)', () => {
252
252
  expect.stringContaining('MODEL QUIRK'), expect.anything());
253
253
  });
254
254
 
255
- test('retry also failing on ANOTHER param error → exactly two attempts, then throw', async () => {
255
+ test('the SAME rejection after the strip → exactly two attempts, then throw (never an infinite ladder)', async () => {
256
256
  const svc = makeService();
257
- const calls = mockBackendRejecting(GPT56, () => true); // always rejects
257
+ const calls = mockBackendRejecting(GPT56, () => true); // always rejects on temperature
258
258
  await expect(svc.sendMessage('gpt-5.6-sol', [{ role: 'user', content: 'hi' }], { ...KEYED }))
259
259
  .rejects.toThrow();
260
- expect(calls).toHaveLength(2); // one learn-retry, never an infinite ladder
260
+ expect(calls).toHaveLength(2); // temperature stripped once; a second identical rejection learns nothing → throw
261
+ });
262
+
263
+ test('gpt-6-astra fixture: two DIFFERENT rejections in one request (forced reasoning_effort, then temperature) → third attempt succeeds', async () => {
264
+ const svc = makeService();
265
+ const ASTRA = "400 Function tools with reasoning_effort are not supported for gpt-6-astra in /v1/chat/completions. To use function tools, use /v1/responses or set reasoning_effort to 'none'.";
266
+ const calls = [];
267
+ global.fetch = jest.fn(async (url, init) => {
268
+ const body = JSON.parse(init.body);
269
+ calls.push(body);
270
+ const reject = (message, param) => ({
271
+ ok: false, status: 400,
272
+ json: async () => ({ error: { message, param, code: 'unsupported_value' } }),
273
+ text: async () => JSON.stringify({ error: { message, param, code: 'unsupported_value' } }),
274
+ headers: { get: () => 'application/json' },
275
+ });
276
+ if (body.options?.reasoning_effort !== 'none') return reject(ASTRA, null);
277
+ if (body.options?.temperature !== undefined) return reject(GPT56, 'temperature');
278
+ return { ok: true, status: 200, json: async () => ({ content: 'ok' }), text: async () => 'ok', headers: { get: () => 'application/json' } };
279
+ });
280
+ const r = await svc.sendMessage('gpt-6-astra', [{ role: 'user', content: 'hi' }], { ...KEYED, temperature: 0.7 });
281
+ expect(r.content).toBe('ok');
282
+ expect(calls).toHaveLength(3);
283
+ expect(calls[2].options.reasoning_effort).toBe('none');
284
+ expect(calls[2].options.temperature).toBeUndefined();
285
+ const { getModelQuirkStore } = await import('../modelQuirks.js');
286
+ expect([...getModelQuirkStore().get('gpt-6-astra')].sort()).toEqual(['reasoning_effort=none', 'temperature']);
261
287
  });
262
288
  });
@@ -17,6 +17,9 @@ import {
17
17
  COMPACTION_CONFIG
18
18
  } from '../utilities/constants.js';
19
19
  import { getModelQuirkStore, classifyParamError, classifyForcedValue, STRIPPABLE_PARAMS } from './modelQuirks.js';
20
+
21
+ /** Distinct parameter rejections a single request may learn from before giving up. */
22
+ const MAX_QUIRK_LEARNS_PER_REQUEST = 4;
20
23
  import { getOllamaService, OLLAMA_MODEL_PREFIX } from './ollamaService.js';
21
24
  import { getDaniService, DANI_MODEL_PREFIX } from './daniService.js';
22
25
  import { SHARED_MEMORY_SCOPE } from './memoryService.js';
@@ -613,7 +616,11 @@ class AIService {
613
616
  });
614
617
  break;
615
618
  } catch (err) {
616
- if (quirkAttempt === 0 && this._learnParamQuirk(model, err, payload.options)) continue;
619
+ // Each learned quirk changes the payload, so several distinct rejections
620
+ // may be resolved within ONE request (gpt-6-astra on the Responses API
621
+ // rejected `temperature`, then `reasoning_effort`: one learn per request
622
+ // cost two failed turns before the third succeeded).
623
+ if (quirkAttempt < MAX_QUIRK_LEARNS_PER_REQUEST && this._learnParamQuirk(model, err, payload.options)) continue;
617
624
  throw err;
618
625
  }
619
626
  }
@@ -776,7 +783,11 @@ class AIService {
776
783
  });
777
784
  break;
778
785
  } catch (err) {
779
- if (quirkAttempt === 0 && this._learnParamQuirk(model, err, payload.options)) continue;
786
+ // Each learned quirk changes the payload, so several distinct rejections
787
+ // may be resolved within ONE request (gpt-6-astra on the Responses API
788
+ // rejected `temperature`, then `reasoning_effort`: one learn per request
789
+ // cost two failed turns before the third succeeded).
790
+ if (quirkAttempt < MAX_QUIRK_LEARNS_PER_REQUEST && this._learnParamQuirk(model, err, payload.options)) continue;
780
791
  throw err;
781
792
  }
782
793
  }