@blockrun/franklin 3.15.35 → 3.15.36

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.
Files changed (2) hide show
  1. package/dist/agent/llm.js +26 -0
  2. package/package.json +1 -1
package/dist/agent/llm.js CHANGED
@@ -8,6 +8,21 @@ import { USER_AGENT } from '../config.js';
8
8
  import { routeRequest, parseRoutingProfile } from '../router/index.js';
9
9
  import { ThinkTagStripper } from './think-tag-stripper.js';
10
10
  import { isNemotronProseModel, stripNemotronProse } from './nemotron-prose-stripper.js';
11
+ // Reasoning-tier models the gateway routes to that reject `tool_choice`
12
+ // outright. Pattern: OpenAI o1/o3 family + DeepSeek's reasoner variant.
13
+ // Add new entries as their 400 errors appear in real sessions; this is
14
+ // a known-bad allowlist, not a guess. Wildcard substring match keeps it
15
+ // resilient to model-revision suffixes (`o1-mini`, `o3-2026-04`, etc.).
16
+ const MODELS_WITHOUT_TOOL_CHOICE_SUBSTR = [
17
+ 'deepseek-reasoner',
18
+ 'openai/o1',
19
+ 'openai/o3',
20
+ ];
21
+ function modelDoesNotSupportToolChoice(model) {
22
+ if (!model)
23
+ return false;
24
+ return MODELS_WITHOUT_TOOL_CHOICE_SUBSTR.some(s => model.includes(s));
25
+ }
11
26
  function parseTimeoutEnv(name) {
12
27
  const raw = process.env[name];
13
28
  const parsed = raw ? Number.parseInt(raw, 10) : NaN;
@@ -309,6 +324,17 @@ export class ModelClient {
309
324
  (!Array.isArray(requestPayload['tools']) || requestPayload['tools'].length === 0)) {
310
325
  delete requestPayload['tool_choice'];
311
326
  }
327
+ // Models that don't support `tool_choice` (reasoning-only families).
328
+ // Verified 2026-05-04 from a real session: grounding-retry forced
329
+ // tool_choice on a request that ended up on deepseek-reasoner, which
330
+ // returned `400 Invalid request: deepseek-reasoner does not support
331
+ // this tool_choice`. Same shape applies to OpenAI o1 / o3 and
332
+ // similar restricted reasoning models. Strip silently — the agent
333
+ // loop's grounding-retry contract already tolerates the field
334
+ // disappearing (it'll re-evaluate next turn).
335
+ if (requestPayload['tool_choice'] !== undefined && modelDoesNotSupportToolChoice(request.model)) {
336
+ delete requestPayload['tool_choice'];
337
+ }
312
338
  // ── GLM-specific optimizations ───────────────────────────────────────────
313
339
  // GLM models work best with temperature=0.8 per official zai spec.
314
340
  // Enable thinking mode only for explicit reasoning variants (-thinking-).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.35",
3
+ "version": "3.15.36",
4
4
  "description": "Franklin — The AI agent with a wallet. Spends USDC autonomously to get real work done. Pay per action, no subscriptions.",
5
5
  "type": "module",
6
6
  "exports": {