fix(opencode): default header timeout to five minutes (#46903)
This commit is contained in:
parent
4eb29a64f0
commit
b04697366f
6 changed files with 47 additions and 40 deletions
|
|
@ -108,11 +108,11 @@ export const Info = Schema.Struct({
|
|||
headerTimeout: Schema.optional(
|
||||
Schema.Union([PositiveInt, Schema.Literal(false)]).annotate({
|
||||
description:
|
||||
"Timeout in milliseconds to wait for response headers. Provider integrations may set defaults. Set to false to disable timeout.",
|
||||
"Timeout in milliseconds to wait for response headers (default: 300000). Set to false to disable timeout.",
|
||||
}),
|
||||
).annotate({
|
||||
description:
|
||||
"Timeout in milliseconds to wait for response headers. Provider integrations may set defaults. Set to false to disable timeout.",
|
||||
"Timeout in milliseconds to wait for response headers (default: 300000). Set to false to disable timeout.",
|
||||
}),
|
||||
chunkTimeout: Schema.optional(
|
||||
Schema.Union([PositiveInt, Schema.Literal(false)]).annotate({
|
||||
|
|
|
|||
|
|
@ -1793,7 +1793,7 @@ const layer = Layer.effect(
|
|||
|
||||
const customFetch = options["fetch"]
|
||||
const chunkTimeout = options["chunkTimeout"] ?? 300_000
|
||||
const headerTimeout = options["headerTimeout"]
|
||||
const headerTimeout = options["headerTimeout"] ?? 300_000
|
||||
delete options["chunkTimeout"]
|
||||
delete options["headerTimeout"]
|
||||
|
||||
|
|
|
|||
|
|
@ -48,40 +48,46 @@ it.live("headerTimeout does not abort delayed SSE body after headers arrive", ()
|
|||
}),
|
||||
)
|
||||
|
||||
it.live("default chunkTimeout is applied at fetch without changing provider options", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedBodyServer(250)),
|
||||
(server) => Effect.sync(() => server.server.close()),
|
||||
)
|
||||
for (const timeout of ["chunkTimeout", "headerTimeout"] as const) {
|
||||
it.live(`default ${timeout} is applied at fetch without changing provider options`, () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedBodyServer(250)),
|
||||
(server) => Effect.sync(() => server.server.close()),
|
||||
)
|
||||
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
const configured = yield* provider.getProvider(ProviderV2.ID.make("test"))
|
||||
const signals: (AbortSignal | null | undefined)[] = []
|
||||
configured.options.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
signals.push(init?.signal)
|
||||
return fetch(input, init)
|
||||
}
|
||||
const model = yield* provider.getModel(ProviderV2.ID.make("test"), ModelV2.ID.make("test-model"))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
yield* Effect.acquireRelease(
|
||||
Effect.promise(() =>
|
||||
language.doStream({ prompt: [{ role: "user", content: [{ type: "text", text: "hello" }] }] }),
|
||||
),
|
||||
(result) => Effect.promise(() => result.stream.cancel()),
|
||||
)
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
const configured = yield* provider.getProvider(ProviderV2.ID.make("test"))
|
||||
const signals: (AbortSignal | null | undefined)[] = []
|
||||
configured.options.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
signals.push(init?.signal)
|
||||
return fetch(input, init)
|
||||
}
|
||||
const model = yield* provider.getModel(ProviderV2.ID.make("test"), ModelV2.ID.make("test-model"))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
yield* Effect.acquireRelease(
|
||||
Effect.promise(() =>
|
||||
language.doStream({ prompt: [{ role: "user", content: [{ type: "text", text: "hello" }] }] }),
|
||||
),
|
||||
(result) => Effect.promise(() => result.stream.cancel()),
|
||||
)
|
||||
|
||||
expect(signals).toHaveLength(1)
|
||||
expect(signals[0]).toBeInstanceOf(AbortSignal)
|
||||
expect(configured.options.chunkTimeout).toBeUndefined()
|
||||
}),
|
||||
{ config: providerConfig(server.url) },
|
||||
)
|
||||
}),
|
||||
)
|
||||
expect(signals).toHaveLength(1)
|
||||
expect(signals[0]).toBeInstanceOf(AbortSignal)
|
||||
expect(configured.options[timeout]).toBeUndefined()
|
||||
}),
|
||||
{
|
||||
config: providerConfig(server.url, {
|
||||
[timeout === "chunkTimeout" ? "headerTimeout" : "chunkTimeout"]: false,
|
||||
}),
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
it.live("configured chunkTimeout raises a retryable response stream error when SSE body stalls", () =>
|
||||
Effect.gen(function* () {
|
||||
|
|
@ -178,7 +184,7 @@ it.live("headerTimeout aborts when response headers do not arrive", () =>
|
|||
}),
|
||||
)
|
||||
|
||||
it.live("headerTimeout is opt-in for non-OpenAI providers", () =>
|
||||
it.live("headerTimeout can be disabled with false for non-OpenAI providers", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedHeaderServer(100)),
|
||||
|
|
@ -197,7 +203,7 @@ it.live("headerTimeout is opt-in for non-OpenAI providers", () =>
|
|||
|
||||
expect(yield* Effect.promise(() => result.text)).toBe("ok")
|
||||
}),
|
||||
{ config: providerConfig(server.url) },
|
||||
{ config: providerConfig(server.url, { headerTimeout: false }) },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1751,7 +1751,7 @@ export type ProviderConfig = {
|
|||
*/
|
||||
timeout?: number | false
|
||||
/**
|
||||
* Timeout in milliseconds to wait for response headers. Provider integrations may set defaults. Set to false to disable timeout.
|
||||
* Timeout in milliseconds to wait for response headers (default: 300000). Set to false to disable timeout.
|
||||
*/
|
||||
headerTimeout?: number | false
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20780,7 +20780,7 @@
|
|||
"enum": [false]
|
||||
}
|
||||
],
|
||||
"description": "Timeout in milliseconds to wait for response headers. Provider integrations may set defaults. Set to false to disable timeout."
|
||||
"description": "Timeout in milliseconds to wait for response headers (default: 300000). Set to false to disable timeout."
|
||||
},
|
||||
"chunkTimeout": {
|
||||
"anyOf": [
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ You can configure the providers and models you want to use in your OpenCode conf
|
|||
|
||||
The `small_model` option configures a separate model for lightweight tasks like title generation. By default, OpenCode tries to use a cheaper model if one is available from your provider, otherwise it falls back to your main model.
|
||||
|
||||
Provider options can include `timeout`, `chunkTimeout`, and `setCacheKey`:
|
||||
Provider options can include `timeout`, `headerTimeout`, `chunkTimeout`, and `setCacheKey`:
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
|
|
@ -392,6 +392,7 @@ Provider options can include `timeout`, `chunkTimeout`, and `setCacheKey`:
|
|||
```
|
||||
|
||||
- `timeout` - Request timeout in milliseconds (default: 300000). Set to `false` to disable.
|
||||
- `headerTimeout` - Timeout in milliseconds to wait for response headers (default: 300000, or 5 minutes). This timer stops once headers arrive and does not limit the streamed response body. Set to `false` to disable.
|
||||
- `chunkTimeout` - Timeout in milliseconds between streamed response chunks (default: 300000, or 5 minutes). If no chunk arrives in time, the request is aborted. Set to `false` to disable.
|
||||
- `setCacheKey` - Ensure a cache key is always set for designated provider.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue