mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 09:26:50 +08:00
Fixes #7112. Task preparation ran `openclaw config file` and `config get agents.list --json` with a hardcoded 5s deadline; on a 2020 Intel MacBook Pro those take 8.4-10.9s and 4.3s every time, so the OpenClaw runtime was unusable there with no way to raise the limit. - Deadline 5s -> 30s, overridable via MULTICA_OPENCLAW_CLI_TIMEOUT / backends.openclaw.cli_timeout, clamped to [1s, 60s] so the worst case (4 serial calls) stays inside the 5-minute preparation budget. - Config discovery cached per daemon profile (on disk, since preparation runs in a one-shot helper process), invalidated by binary identity, OpenClaw path env, active config stat, and a 60s TTL. Only complete successes are cached; any cache fault degrades to a live CLI run. - The timeout is classified structurally into a new platform-side, non-retryable reason (runtime_cli_timeout) instead of being text-matched into agent_error.provider_network, which showed "check your network" copy and auto-retried a deterministic local failure. Copy added for web/desktop/mobile; a NormalizeDaemonReason shim upgrades reports from older daemons.
51 lines
2.0 KiB
Go
51 lines
2.0 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/multica-ai/multica/server/internal/daemon/execenv"
|
|
"github.com/multica-ai/multica/server/pkg/taskfailure"
|
|
)
|
|
|
|
// TestTaskRunFailureReasonLabelsOpenclawCLITimeout is the daemon half of the
|
|
// #7112 fix. The error text ends in "context deadline exceeded", which
|
|
// taskfailure.Classify maps to agent_error.provider_network — so before the
|
|
// sentinel, a local `openclaw config file` stall was reported to the user as a
|
|
// dropped connection to the model provider, and auto-retried because that
|
|
// reason is on the retry allowlist. The stall is local and deterministic: the
|
|
// retry re-pays the same 8-11s and fails identically.
|
|
func TestTaskRunFailureReasonLabelsOpenclawCLITimeout(t *testing.T) {
|
|
err := fmt.Errorf(
|
|
"prepare execution environment: execenv: prepare openclaw config: locate openclaw active config: %w",
|
|
fmt.Errorf("openclaw config file: context deadline exceeded (process: signal: killed): %w", execenv.ErrOpenclawCLITimeout),
|
|
)
|
|
|
|
want := taskfailure.ReasonRuntimeCLITimeout.String()
|
|
if got := taskRunFailureReason(err); got != want {
|
|
t.Errorf("taskRunFailureReason = %q, want %q", got, want)
|
|
}
|
|
|
|
// The reason must stay off the auto-retry allowlist (asserted next to that
|
|
// table in internal/service): nothing about the host changes between
|
|
// attempts, so a retry only burns another full preparation before failing
|
|
// the same way.
|
|
}
|
|
|
|
// TestTaskRunFailureReasonKeepsProviderDeadline pins the other side of the
|
|
// same boundary: a real provider-side deadline (no sentinel) must keep landing
|
|
// in the retryable provider bucket.
|
|
func TestTaskRunFailureReasonKeepsProviderDeadline(t *testing.T) {
|
|
err := fmt.Errorf("stream from provider: %w", context.DeadlineExceeded)
|
|
|
|
want := taskfailure.ReasonAgentProviderNetwork.String()
|
|
if got := taskRunFailureReason(err); got != want {
|
|
t.Errorf("taskRunFailureReason = %q, want %q", got, want)
|
|
}
|
|
if errors.Is(err, execenv.ErrOpenclawCLITimeout) {
|
|
t.Error("a bare context deadline must not match the openclaw CLI sentinel")
|
|
}
|
|
}
|