Skip to content

feat(models): add per-provider timeoutSeconds config#65143

Closed
uninhibited-scholar wants to merge 3 commits into
openclaw:mainfrom
uninhibited-scholar:feat/provider-timeout-seconds
Closed

feat(models): add per-provider timeoutSeconds config#65143
uninhibited-scholar wants to merge 3 commits into
openclaw:mainfrom
uninhibited-scholar:feat/provider-timeout-seconds

Conversation

@uninhibited-scholar

Copy link
Copy Markdown

Summary

Allow each model provider to specify a custom stream timeout via timeoutSeconds in the provider config. Useful for slow local providers (e.g. Ollama, LM Studio) that need longer than the 30-minute default.

Changes

  • Add timeoutSeconds?: number field to ModelProviderConfig type
  • Add Zod validation (z.number().int().positive().optional())
  • Read provider-specific timeout in runEmbeddedAttempt and pass to ensureGlobalUndiciStreamTimeouts

Usage Example

{
  "models": {
    "providers": {
      "ollama": {
        "baseUrl": "http://localhost:11434",
        "timeoutSeconds": 600,  // 10 minutes for slow local inference
        "models": [{ "id": "llama3" }]
      }
    }
  }
}

Testing

  • Type definition and Zod schema are consistent
  • When timeoutSeconds is not set, behavior is unchanged (falls through to 30-min default)
  • Uses the existing ensureGlobalUndiciStreamTimeouts({ timeoutMs }) API

Notes

Clean rewrite of #63191 — this PR contains only the timeoutSeconds feature without unrelated changes (tar bump, parse.ts refactor, etc.).

Allow each model provider to specify a custom stream timeout via
timeoutSeconds in the provider config. Useful for slow local providers
(e.g. Ollama, LM Studio) that need longer than the 30-minute default.

Changes:
- Add timeoutSeconds field to ModelProviderConfig type
- Add Zod validation (positive integer)
- Read provider-specific timeout in runEmbeddedAttempt

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37f5774f20

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +350 to +351
ensureGlobalUndiciStreamTimeouts(
providerTimeoutSeconds != null ? { timeoutMs: providerTimeoutSeconds * 1000 } : undefined,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scope timeout changes to per-request dispatcher

This call applies provider-specific timeout by invoking ensureGlobalUndiciStreamTimeouts, but that helper rewrites Undici's process-global dispatcher. If two embedded attempts overlap (for example, different sessions/providers running at the same time), the later attempt overwrites the timeout for the whole process, so the first attempt and other concurrent network calls can run with the wrong timeout and fail unpredictably. Per-provider timeout needs to be bound to the provider request path (or a per-request dispatcher) instead of mutating global state on each attempt.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a timeoutSeconds field to ModelProviderConfig and the matching Zod schema, then reads it in runEmbeddedAttempt to pass a per-provider timeout to the global undici dispatcher — useful for slow local providers like Ollama or LM Studio.

  • The schema.help.ts file is missing the \"models.providers.*.timeoutSeconds\" help entry that every other ModelProviderSchema field has, and docs/.generated/config-baseline.sha256 was not regenerated. The pnpm config:docs:check CI gate will fail. Run pnpm config:docs:gen and commit the updated hash file.

Confidence Score: 4/5

Safe to merge after adding the missing help entry and regenerating the config baseline sha256.

One P1 finding: the config doc baseline was not regenerated after modifying ModelProviderSchema, and the help text for the new field is absent from schema.help.ts — this will cause pnpm config:docs:check to fail in CI. The feature logic itself is correct.

src/config/schema.help.ts (missing help entry) and docs/.generated/config-baseline.sha256 (stale hash)

Comments Outside Diff (1)

  1. src/config/schema.help.ts, line 753 (link)

    P1 Missing help entry for models.providers.*.timeoutSeconds

    Every field in ModelProviderSchema has a corresponding entry in FIELD_HELP (see the block starting at line 739). Adding timeoutSeconds to the Zod schema without adding a matching "models.providers.*.timeoutSeconds" entry here will cause pnpm config:docs:check to detect baseline drift and fail. The sha256 in docs/.generated/config-baseline.sha256 is also unchanged from the pre-PR state, confirming that pnpm config:docs:gen was not run.

    Add the missing help entry after line 753 (after authHeader) and then run pnpm config:docs:gen to update the hash file:

      "models.providers.*.authHeader":
        "When true, credentials are sent via the HTTP Authorization header even if alternate auth is possible. Use this only when your provider or proxy explicitly requires Authorization forwarding.",
      "models.providers.*.timeoutSeconds":
        "Per-provider stream timeout in seconds. Overrides the global 30-minute default. Use this for slow local providers (e.g. Ollama, LM Studio) that require longer inference time.",
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/config/schema.help.ts
    Line: 753
    
    Comment:
    **Missing help entry for `models.providers.*.timeoutSeconds`**
    
    Every field in `ModelProviderSchema` has a corresponding entry in `FIELD_HELP` (see the block starting at line 739). Adding `timeoutSeconds` to the Zod schema without adding a matching `"models.providers.*.timeoutSeconds"` entry here will cause `pnpm config:docs:check` to detect baseline drift and fail. The sha256 in `docs/.generated/config-baseline.sha256` is also unchanged from the pre-PR state, confirming that `pnpm config:docs:gen` was not run.
    
    Add the missing help entry after line 753 (after `authHeader`) and then run `pnpm config:docs:gen` to update the hash file:
    
    ```ts
      "models.providers.*.authHeader":
        "When true, credentials are sent via the HTTP Authorization header even if alternate auth is possible. Use this only when your provider or proxy explicitly requires Authorization forwarding.",
      "models.providers.*.timeoutSeconds":
        "Per-provider stream timeout in seconds. Overrides the global 30-minute default. Use this for slow local providers (e.g. Ollama, LM Studio) that require longer inference time.",
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/config/schema.help.ts
Line: 753

Comment:
**Missing help entry for `models.providers.*.timeoutSeconds`**

Every field in `ModelProviderSchema` has a corresponding entry in `FIELD_HELP` (see the block starting at line 739). Adding `timeoutSeconds` to the Zod schema without adding a matching `"models.providers.*.timeoutSeconds"` entry here will cause `pnpm config:docs:check` to detect baseline drift and fail. The sha256 in `docs/.generated/config-baseline.sha256` is also unchanged from the pre-PR state, confirming that `pnpm config:docs:gen` was not run.

Add the missing help entry after line 753 (after `authHeader`) and then run `pnpm config:docs:gen` to update the hash file:

```ts
  "models.providers.*.authHeader":
    "When true, credentials are sent via the HTTP Authorization header even if alternate auth is possible. Use this only when your provider or proxy explicitly requires Authorization forwarding.",
  "models.providers.*.timeoutSeconds":
    "Per-provider stream timeout in seconds. Overrides the global 30-minute default. Use this for slow local providers (e.g. Ollama, LM Studio) that require longer inference time.",
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run/attempt.ts
Line: 348-349

Comment:
**Unnecessary type cast — `timeoutSeconds` is already in `ModelProviderConfig`**

`params.config?.models?.providers?.[params.provider]` resolves to `ModelProviderConfig | undefined`, and `ModelProviderConfig` now declares `timeoutSeconds?: number`, so the `as { timeoutSeconds?: number } | undefined` cast is redundant and silently bypasses TypeScript's structural checking for that expression. Drop the cast:

```suggestion
  const providerTimeoutSeconds =
    params.config?.models?.providers?.[params.provider]?.timeoutSeconds;
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(models): add per-provider timeoutSe..." | Re-trigger Greptile

Comment on lines +348 to +349
(params.config?.models?.providers?.[params.provider] as { timeoutSeconds?: number } | undefined)
?.timeoutSeconds;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unnecessary type cast — timeoutSeconds is already in ModelProviderConfig

params.config?.models?.providers?.[params.provider] resolves to ModelProviderConfig | undefined, and ModelProviderConfig now declares timeoutSeconds?: number, so the as { timeoutSeconds?: number } | undefined cast is redundant and silently bypasses TypeScript's structural checking for that expression. Drop the cast:

Suggested change
(params.config?.models?.providers?.[params.provider] as { timeoutSeconds?: number } | undefined)
?.timeoutSeconds;
const providerTimeoutSeconds =
params.config?.models?.providers?.[params.provider]?.timeoutSeconds;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run/attempt.ts
Line: 348-349

Comment:
**Unnecessary type cast — `timeoutSeconds` is already in `ModelProviderConfig`**

`params.config?.models?.providers?.[params.provider]` resolves to `ModelProviderConfig | undefined`, and `ModelProviderConfig` now declares `timeoutSeconds?: number`, so the `as { timeoutSeconds?: number } | undefined` cast is redundant and silently bypasses TypeScript's structural checking for that expression. Drop the cast:

```suggestion
  const providerTimeoutSeconds =
    params.config?.models?.providers?.[params.provider]?.timeoutSeconds;
```

How can I resolve this? If you propose a fix, please make it concise.

- Add models.providers.*.timeoutSeconds help entry to schema.help.ts
- Regenerate config-baseline.sha256
- Remove redundant type cast in attempt.ts (ModelProviderConfig
  already declares timeoutSeconds)
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Apr 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 471db1e354

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

injectNumCtxForOpenAICompat: z.boolean().optional(),
headers: z.record(z.string(), SecretInputSchema.register(sensitive)).optional(),
authHeader: z.boolean().optional(),
timeoutSeconds: z.number().int().positive().optional(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Regenerate base config schema for timeoutSeconds

Adding timeoutSeconds to ModelProviderSchema updates parsing, but the runtime schema payload comes from src/config/schema.base.generated.ts via src/config/schema.ts, and this commit does not regenerate that artifact. The generated schema still omits models.providers.*.timeoutSeconds, so Control UI / schema consumers cannot discover or configure the new field even though the parser accepts it in raw config.

Useful? React with 👍 / 👎.

@sikc231

sikc231 commented Apr 24, 2026

Copy link
Copy Markdown

Hey, any updates on this feature?

@clawsweeper

clawsweeper Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Codex automated review: keeping this open.

Keep this PR open. Current main still does not implement models.providers.*.timeoutSeconds; the PR remains a relevant implementation candidate, but it should be revised before merge because the current approach configures process-global Undici timeout state and the generated config schema surface is not fully updated.

Best possible solution:

Keep the PR open and adapt it. The best path is to add models.providers.*.timeoutSeconds consistently across the config type, strict Zod schema, help/labels, generated schema artifacts, docs, and tests, then resolve the selected provider's value and pass it through the provider request/transport fetch path as a per-request timeout instead of changing global Undici dispatcher state.

What I checked:

  • Current main lacks the config type field: ModelProviderConfig currently includes baseUrl, auth/API/header fields, request, and models, with no timeoutSeconds member. (src/config/types.models.ts:117, 4878d3e059ce)
  • Current main rejects the config key: ModelProviderSchema is strict and lists provider properties without timeoutSeconds, so raw config using the proposed key is not accepted on current main. (src/config/zod-schema.core.ts:352, 4878d3e059ce)
  • Generated config metadata omits the field: The generated metadata has models.providers.*.authHeader followed by models.providers.*.request; there is no models.providers.*.timeoutSeconds entry for schema/UI consumers. (src/config/schema.base.generated.ts:26490, 4878d3e059ce)
  • Runtime still uses run-level global timeout tuning: runEmbeddedAttempt calls configureEmbeddedAttemptHttpRuntime({ timeoutMs: params.timeoutMs }), and that helper calls ensureGlobalUndiciStreamTimeouts with the run timeout floor rather than any provider-specific config. (src/agents/pi-embedded-runner/run/attempt.ts:580, 4878d3e059ce)
  • Per-request timeout seam already exists: buildGuardedModelFetch(model, timeoutMs) can pass a timeout to fetchWithSsrFGuard, whose guarded dispatcher path applies that timeout per request; however OpenAI, Anthropic, and Google transport call sites currently call it with only model. (src/agents/provider-transport-fetch.ts:157, 4878d3e059ce)
  • Tests cover the seam, not provider config resolution: Existing provider transport tests verify an explicit timeoutMs argument is threaded into fetchWithSsrFGuard, which supports adapting the PR to resolve provider config and pass it through this path. (src/agents/provider-transport-fetch.test.ts:78, 4878d3e059ce)

Remaining risk / open question:

  • Merging the PR as currently described could make concurrent provider calls inherit the wrong timeout because it mutates process-global Undici dispatcher state per embedded attempt.
  • The config metadata path must include help, labels, generated schema artifacts, and baseline hashes so Control UI and schema consumers can discover the field.
  • A complete fix needs focused tests for provider-config timeout resolution and per-request transport wiring, not only type/schema additions.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against 4878d3e059ce.

@steipete

Copy link
Copy Markdown
Contributor

Thanks @uninhibite-scholar — this was the right config surface.

Superseded on main by 18b76e3. I kept models.providers.*.timeoutSeconds, but wired it through the resolved runtime model and per-request guarded fetch path instead of mutating the process-global Undici dispatcher during an agent attempt. That means provider timeouts now apply to the model HTTP request only, including guarded fetch abort handling plus Undici connect/header/body timeouts.

Validation on the landed fix:

  • pnpm test src/agents/pi-embedded-runner/model.test.ts src/agents/provider-transport-fetch.test.ts extensions/ollama/src/stream-runtime.test.ts src/infra/net/ssrf.dispatcher.test.ts src/infra/net/fetch-guard.ssrf.test.ts
  • pnpm config:schema:check && pnpm config:docs:check
  • pnpm check:changed
  • live local Ollama: OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_OLLAMA=1 OPENCLAW_LIVE_OLLAMA_MODEL=llama3.2:latest OPENCLAW_LIVE_OLLAMA_WEB_SEARCH=0 pnpm test:live -- extensions/ollama/ollama.live.test.ts

Closing this PR as superseded by the maintainer-owned implementation. Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants