Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/src/cron/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 14 kB image not shown  

Quelle  isolated-agent.model-formatting.test.ts

  Sprache: JAVA
 

import { beforeEach, describe, expect, it, vi } from "vitest";
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";

const {
  loadModelCatalogMock,
  getModelRefStatusMock,
  normalizeModelSelectionMock,
  resolveAllowedModelRefMock,
  resolveConfiguredModelRefMock,
  resolveHooksGmailModelMock,
} = vi.hoisted(() => ({
  loadModelCatalogMock: vi.fn(),
  getModelRefStatusMock: vi.fn(),
  normalizeModelSelectionMock: vi.fn((value: unknown) => {
    if (typeof value === "string" && value.trim()) {
      return value.trim();
    }
    if (
      value &&
      typeof value === "object" &&
      typeof (value as { primary?: unknown }).primary === "string" &&
      (value as { primary: string }).primary.trim()
    ) {
      return (value as { primary: string }).primary.trim();
    }
    return undefined;
  }),
  resolveAllowedModelRefMock: vi.fn(),
  resolveConfiguredModelRefMock: vi.fn(),
  resolveHooksGmailModelMock: vi.fn(),
}));

vi.mock("./isolated-agent/run-model-selection.runtime.js", () => ({
  DEFAULT_MODEL: "claude-opus-4-6",
  DEFAULT_PROVIDER: "anthropic",
  getModelRefStatus: getModelRefStatusMock,
  loadModelCatalog: loadModelCatalogMock,
  normalizeModelSelection: normalizeModelSelectionMock,
  resolveAllowedModelRef: resolveAllowedModelRefMock,
  resolveConfiguredModelRef: resolveConfiguredModelRefMock,
  resolveHooksGmailModel: resolveHooksGmailModelMock,
}));

import { resolveCronModelSelection } from "./isolated-agent/model-selection.js";

const DEFAULT_MESSAGE = "do it";

type AgentTurnPayload = {
  kind: "agentTurn";
  message: string;
  model?: string;
};

type SelectModelOptions = {
  cfg?: Record<string, unknown>;
  agentConfigOverride?: {
    model?: unknown;
    subagents?: {
      model?: unknown;
    };
  };
  payload?: AgentTurnPayload;
  sessionEntry?: {
    modelOverride?: string;
    providerOverride?: string;
  };
  isGmailHook?: boolean;
};

function parseModelRef(raw: string): { provider: string; model: string } | { error: string } {
  const trimmed = raw.trim();
  const slash = trimmed.indexOf("/");
  if (slash <= 0 || slash === trimmed.length - 1) {
    return { error: "invalid model" };
  }

  const providerRaw = trimmed.slice(0, slash).trim().toLowerCase();
  const modelRaw = trimmed.slice(slash + 1).trim();
  if (!providerRaw || !modelRaw) {
    return { error: "invalid model" };
  }

  const provider = providerRaw === "bedrock" ? "amazon-bedrock" : providerRaw;
  const model = provider === "anthropic" && modelRaw === "opus-4.5" ? "claude-opus-4-5" : modelRaw;
  return { provider, model };
}

function resolveConfiguredModelForTest(cfg: Record<string, unknown>): {
  provider: string;
  model: string;
} {
  const modelValue = (cfg.agents as { defaults?: { model?: unknown } } | undefined)?.defaults
    ?.model;
  const rawModel =
    typeof modelValue === "string"
      ? modelValue
      : typeof modelValue === "object" &&
          modelValue &&
          typeof (modelValue as { primary?: unknown }).primary === "string"
        ? (modelValue as { primary: string }).primary
        : undefined;

  if (typeof rawModel === "string") {
    const parsed = parseModelRef(rawModel);
    if (!("error" in parsed)) {
      return parsed;
    }
  }

  return { provider: DEFAULT_PROVIDER, model: DEFAULT_MODEL };
}

function defaultPayload(): AgentTurnPayload {
  return {
    kind: "agentTurn",
    message: DEFAULT_MESSAGE,
  };
}

async function selectModel(options: SelectModelOptions = {}) {
  const cfg = options.cfg ?? {};
  return resolveCronModelSelection({
    cfg: cfg as never,
    cfgWithAgentDefaults: cfg as never,
    agentConfigOverride: options.agentConfigOverride,
    sessionEntry: options.sessionEntry ?? {},
    payload: options.payload ?? defaultPayload(),
    isGmailHook: options.isGmailHook ?? false,
  });
}

async function expectSelectedModel(
  options: SelectModelOptions,
  expected: { provider: string; model: string },
) {
  const result = await selectModel(options);
  expect(result).toEqual({ ok: true, ...expected });
}

async function expectDefaultSelectedModel(options: SelectModelOptions = {}) {
  await expectSelectedModel(options, { provider: DEFAULT_PROVIDER, model: DEFAULT_MODEL });
}

describe("cron model formatting and precedence edge cases", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    loadModelCatalogMock.mockResolvedValue([]);
    getModelRefStatusMock.mockReturnValue({ allowed: false });
    resolveHooksGmailModelMock.mockReturnValue(null);
    resolveConfiguredModelRefMock.mockImplementation(({ cfg }: { cfg?: Record<string, unknown> }) =>
      resolveConfiguredModelForTest(cfg ?? {}),
    );
    resolveAllowedModelRefMock.mockImplementation(({ raw }: { raw: string }) => {
      const parsed = parseModelRef(raw);
      return "error" in parsed ? parsed : { ref: parsed };
    });
  });

  describe("parseModelRef formatting", () => {
    it("splits standard provider/model", async () => {
      await expectSelectedModel(
        {
          payload: { kind: "agentTurn", message: DEFAULT_MESSAGE, model: "openai/gpt-4.1-mini" },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );
    });

    it("handles leading/trailing whitespace in model string", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "  openai/gpt-4.1-mini  ",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );
    });

    it("handles openrouter nested provider paths", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "openrouter/meta-llama/llama-3.3-70b:free",
          },
        },
        { provider: "openrouter", model: "meta-llama/llama-3.3-70b:free" },
      );
    });

    it("rejects model with trailing slash (empty model name)", async () => {
      await expect(
        selectModel({
          payload: { kind: "agentTurn", message: DEFAULT_MESSAGE, model: "openai/" },
        }),
      ).resolves.toEqual({ ok: false, error: "invalid model" });
    });

    it("rejects model with leading slash (empty provider)", async () => {
      await expect(
        selectModel({
          payload: { kind: "agentTurn", message: DEFAULT_MESSAGE, model: "/gpt-4.1-mini" },
        }),
      ).resolves.toEqual({ ok: false, error: "invalid model" });
    });

    it("normalizes provider casing", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "OpenAI/gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );
    });

    it("normalizes anthropic model aliases", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "anthropic/opus-4.5",
          },
        },
        { provider: "anthropic", model: "claude-opus-4-5" },
      );
    });

    it("normalizes bedrock provider alias", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "bedrock/claude-sonnet-4-6",
          },
        },
        { provider: "amazon-bedrock", model: "claude-sonnet-4-6" },
      );
    });
  });

  describe("model precedence isolation", () => {
    it("job payload model overrides default (anthropic -> openai)", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "openai/gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );
    });

    it("session override applies when no job payload model is present", async () => {
      await expectSelectedModel(
        {
          sessionEntry: {
            providerOverride: "openai",
            modelOverride: "gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );
    });

    it("job payload model wins over conflicting session override", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "anthropic/claude-sonnet-4-6",
          },
          sessionEntry: {
            providerOverride: "openai",
            modelOverride: "gpt-4.1-mini",
          },
        },
        { provider: "anthropic", model: "claude-sonnet-4-6" },
      );
    });

    it("falls through to default when no override is present", async () => {
      await expectDefaultSelectedModel();
    });
  });

  describe("sequential model switches (CI failure regression)", () => {
    it("openai override -> session openai -> job anthropic: each step resolves correctly", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "openai/gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );

      await expectSelectedModel(
        {
          sessionEntry: {
            providerOverride: "openai",
            modelOverride: "gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );

      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "anthropic/claude-opus-4-6",
          },
          sessionEntry: {
            providerOverride: "openai",
            modelOverride: "gpt-4.1-mini",
          },
        },
        { provider: "anthropic", model: "claude-opus-4-6" },
      );
    });

    it("provider does not leak between isolated sequential runs", async () => {
      await expectSelectedModel(
        {
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "openai/gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );

      await expectDefaultSelectedModel();
    });
  });

  describe("stored session overrides", () => {
    it("stored modelOverride/providerOverride are applied", async () => {
      await expectSelectedModel(
        {
          sessionEntry: {
            providerOverride: "openai",
            modelOverride: "gpt-4.1-mini",
          },
        },
        { provider: "openai", model: "gpt-4.1-mini" },
      );
    });

    it("default remains when store has no override", async () => {
      await expectDefaultSelectedModel({ sessionEntry: {} });
    });
  });

  describe("whitespace and empty model strings", () => {
    it("whitespace-only model treated as unset (falls to default)", async () => {
      await expectDefaultSelectedModel({
        payload: { kind: "agentTurn", message: DEFAULT_MESSAGE, model: "   " },
      });
    });

    it("empty string model treated as unset", async () => {
      await expectDefaultSelectedModel({
        payload: { kind: "agentTurn", message: DEFAULT_MESSAGE, model: "" },
      });
    });

    it("whitespace-only session modelOverride is ignored", async () => {
      await expectDefaultSelectedModel({
        sessionEntry: {
          providerOverride: "openai",
          modelOverride: "   ",
        },
      });
    });
  });

  describe("config model format variations", () => {
    it("default model as string 'provider/model'", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: "openai/gpt-4.1",
              },
            },
          },
        },
        { provider: "openai", model: "gpt-4.1" },
      );
    });

    it("default model as object with primary field", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: { primary: "openai/gpt-4.1" },
              },
            },
          },
        },
        { provider: "openai", model: "gpt-4.1" },
      );
    });

    it("job override switches away from object default", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: { primary: "openai/gpt-4.1" },
              },
            },
          },
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "anthropic/claude-sonnet-4-6",
          },
        },
        { provider: "anthropic", model: "claude-sonnet-4-6" },
      );
    });

    it("uses agents.defaults.subagents.model when set", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: "anthropic/claude-sonnet-4-6",
                subagents: { model: "ollama/llama3.2:3b" },
              },
            },
          },
        },
        { provider: "ollama", model: "llama3.2:3b" },
      );
    });

    it("supports subagents.model with {primary} object format", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: "anthropic/claude-sonnet-4-6",
                subagents: { model: { primary: "google/gemini-2.5-flash" } },
              },
            },
          },
        },
        { provider: "google", model: "gemini-2.5-flash" },
      );
    });

    it("job payload model override takes precedence over subagents.model", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: "anthropic/claude-sonnet-4-6",
                subagents: { model: "ollama/llama3.2:3b" },
              },
            },
          },
          payload: {
            kind: "agentTurn",
            message: DEFAULT_MESSAGE,
            model: "openai/gpt-4o",
          },
        },
        { provider: "openai", model: "gpt-4o" },
      );
    });

    it("prefers the agent model over agents.defaults.subagents.model", async () => {
      await expectSelectedModel(
        {
          cfg: {
            agents: {
              defaults: {
                model: "anthropic/claude-sonnet-4-6",
                subagents: { model: "ollama/llama3.2:3b" },
              },
            },
          },
          agentConfigOverride: {
            model: { primary: "anthropic/claude-opus-4-6" },
          },
        },
        { provider: "anthropic", model: "claude-opus-4-6" },
      );
    });
  });
});

Messung V0.5 in Prozent
C=96 H=98 G=96

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-05-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.