fix(providers): enabled append-only auto for xiaomi
Centralized append-only context auto-mode resolution so SDK sessions, interactive sessions, and the status display use the same provider checks. Auto mode now enables for Xiaomi/SGLang hosts and explicit stored-request compat signals while preserving DeepSeek behavior. Added focused regression coverage for Xiaomi Token Plan SGLang HiCache endpoints and explicit on/off behavior. Fixes #1851
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `provider.appendOnlyContext: "auto"` staying inactive for Xiaomi Token Plan/SGLang endpoints, preserving prefix-cache hits without forcing append-only mode globally ([#1851](https://github.com/can1357/oh-my-pi/issues/1851)).
|
||||
|
||||
## [15.9.0] - 2026-06-04
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/** Provider metadata needed to resolve append-only context mode. */
|
||||
export interface AppendOnlyContextModel {
|
||||
provider: string;
|
||||
baseUrl: string;
|
||||
compat?: object;
|
||||
}
|
||||
|
||||
function isXiaomiHost(baseUrl: string): boolean {
|
||||
try {
|
||||
const host = new URL(baseUrl).hostname;
|
||||
return host === "xiaomimimo.com" || host.endsWith(".xiaomimimo.com");
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldAutoEnableAppendOnlyContext(model: AppendOnlyContextModel | null | undefined): boolean {
|
||||
if (!model) return false;
|
||||
if (model.provider === "deepseek") return true;
|
||||
if (isXiaomiHost(model.baseUrl)) return true;
|
||||
return !!model.compat && "supportsStore" in model.compat && model.compat.supportsStore === true;
|
||||
}
|
||||
|
||||
/** Resolves whether append-only context should be active for a model and setting. */
|
||||
export function shouldEnableAppendOnlyContext(
|
||||
setting: "auto" | "on" | "off" | undefined,
|
||||
model: AppendOnlyContextModel | null | undefined,
|
||||
): boolean {
|
||||
switch (setting ?? "auto") {
|
||||
case "on":
|
||||
return true;
|
||||
case "off":
|
||||
return false;
|
||||
default:
|
||||
return shouldAutoEnableAppendOnlyContext(model);
|
||||
}
|
||||
}
|
||||
@@ -3057,9 +3057,9 @@ export const SETTINGS_SCHEMA = {
|
||||
tab: "providers",
|
||||
label: "Append-Only Context",
|
||||
description:
|
||||
"Cache system prompt + tool specs and keep an append-only message log so provider prefix caches (DeepSeek, Anthropic) hit at maximum rate. Auto enables for DeepSeek.",
|
||||
"Cache system prompt + tool specs and keep an append-only message log so provider prefix caches (DeepSeek, Xiaomi/SGLang, Anthropic) hit at maximum rate. Auto enables for known prefix-cache providers.",
|
||||
options: [
|
||||
{ value: "auto", label: "Auto", description: "Enable for DeepSeek (recommended)" },
|
||||
{ value: "auto", label: "Auto", description: "Enable for known prefix-cache providers (recommended)" },
|
||||
{ value: "on", label: "On", description: "Always enable append-only context" },
|
||||
{ value: "off", label: "Off", description: "Disable append-only context" },
|
||||
],
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { Loader, Markdown, padding, Spacer, Text, visibleWidth } from "@oh-my-pi/pi-tui";
|
||||
import { formatDuration, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { $ } from "bun";
|
||||
import { shouldEnableAppendOnlyContext } from "../../config/append-only-context-mode";
|
||||
import { loadCustomShare } from "../../export/custom-share";
|
||||
import type { CompactOptions } from "../../extensibility/extensions/types";
|
||||
import {
|
||||
@@ -397,10 +398,10 @@ export class CommandController {
|
||||
// Append-only context
|
||||
{
|
||||
const setting = this.ctx.settings.get("provider.appendOnlyContext") ?? "auto";
|
||||
const provider = this.ctx.session.model?.provider;
|
||||
const mode = setting === "on" ? true : setting === "off" ? false : provider === "deepseek";
|
||||
const model = this.ctx.session.model;
|
||||
const mode = shouldEnableAppendOnlyContext(setting, model);
|
||||
const activeLabel = mode ? theme.fg("success", "active") : theme.fg("dim", "inactive");
|
||||
const settingLabel = setting === "auto" ? `${setting} (${provider ?? "?"})` : setting;
|
||||
const settingLabel = setting === "auto" ? `${setting} (${model?.provider ?? "?"})` : setting;
|
||||
info += `${theme.fg("dim", "Append-Only:")} ${activeLabel} (setting: ${settingLabel})\n`;
|
||||
}
|
||||
info += `${theme.bold("Tokens")}\n`;
|
||||
|
||||
@@ -39,6 +39,7 @@ import { createAutoresearchExtension } from "./autoresearch";
|
||||
import { loadCapability } from "./capability";
|
||||
import { type Rule, ruleCapability, setActiveRules } from "./capability/rule";
|
||||
import { bucketRules } from "./capability/rule-buckets";
|
||||
import { shouldEnableAppendOnlyContext } from "./config/append-only-context-mode";
|
||||
import { ModelRegistry } from "./config/model-registry";
|
||||
import {
|
||||
formatModelString,
|
||||
@@ -646,24 +647,6 @@ function registerPythonCleanup(): void {
|
||||
postmortem.register("python-cleanup", disposeAllKernelSessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve whether to enable append-only context mode based on the setting and provider.
|
||||
*
|
||||
* - `"on"` → always enable
|
||||
* - `"off"` → never enable
|
||||
* - `"auto"` → enable for DeepSeek (prefix-caching provider)
|
||||
*/
|
||||
function resolveAppendOnlyMode(setting: "auto" | "on" | "off" | undefined, provider: string): boolean {
|
||||
switch (setting ?? "auto") {
|
||||
case "on":
|
||||
return true;
|
||||
case "off":
|
||||
return false;
|
||||
default:
|
||||
return provider === "deepseek";
|
||||
}
|
||||
}
|
||||
|
||||
function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
||||
const definition: ToolDefinition & { [TOOL_DEFINITION_MARKER]: true } = {
|
||||
name: tool.name,
|
||||
@@ -2028,7 +2011,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
getToolChoice: () => session?.nextToolChoice(),
|
||||
telemetry: options.telemetry,
|
||||
appendOnlyContext: model
|
||||
? resolveAppendOnlyMode(settings.get("provider.appendOnlyContext"), model.provider)
|
||||
? shouldEnableAppendOnlyContext(settings.get("provider.appendOnlyContext"), model)
|
||||
? new AppendOnlyContextManager()
|
||||
: undefined
|
||||
: undefined,
|
||||
|
||||
@@ -100,6 +100,7 @@ import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../a
|
||||
import { classifyDifficulty } from "../auto-thinking/classifier";
|
||||
import { reset as resetCapabilities } from "../capability";
|
||||
import type { Rule } from "../capability/rule";
|
||||
import { shouldEnableAppendOnlyContext } from "../config/append-only-context-mode";
|
||||
import { MODEL_ROLE_IDS, type ModelRegistry } from "../config/model-registry";
|
||||
import {
|
||||
extractExplicitThinkingSelector,
|
||||
@@ -6735,8 +6736,8 @@ export class AgentSession {
|
||||
*/
|
||||
#syncAppendOnlyContext(model: Model | null | undefined): void {
|
||||
const setting = this.settings.get("provider.appendOnlyContext") ?? "auto";
|
||||
const enable = shouldEnableAppendOnlyContext(setting, model);
|
||||
const providerId = model?.provider;
|
||||
const enable = setting === "on" || (setting === "auto" && providerId === "deepseek");
|
||||
const prev = this.#lastAppendOnlyResolution;
|
||||
if (prev && prev.enable === enable && prev.providerId === providerId) return;
|
||||
this.#lastAppendOnlyResolution = { enable, providerId };
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { shouldEnableAppendOnlyContext } from "@oh-my-pi/pi-coding-agent/config/append-only-context-mode";
|
||||
|
||||
const XIAOMI_TOKEN_PLAN_ANTHROPIC = {
|
||||
provider: "xiaomi-token-plan-sgp",
|
||||
baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic",
|
||||
};
|
||||
|
||||
const GENERIC_PROXY = {
|
||||
provider: "generic-proxy",
|
||||
baseUrl: "https://llm.example.com/v1",
|
||||
};
|
||||
|
||||
describe("shouldEnableAppendOnlyContext", () => {
|
||||
test("honors explicit on and off settings", () => {
|
||||
expect(shouldEnableAppendOnlyContext("on", GENERIC_PROXY)).toBe(true);
|
||||
expect(shouldEnableAppendOnlyContext("off", { provider: "deepseek", baseUrl: "https://api.deepseek.com" })).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("auto enables for DeepSeek", () => {
|
||||
expect(shouldEnableAppendOnlyContext("auto", { provider: "deepseek", baseUrl: "https://api.deepseek.com" })).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("auto enables for Xiaomi Token Plan SGLang HiCache endpoints", () => {
|
||||
expect(shouldEnableAppendOnlyContext("auto", XIAOMI_TOKEN_PLAN_ANTHROPIC)).toBe(true);
|
||||
});
|
||||
|
||||
test("auto enables when model compat explicitly supports stored requests", () => {
|
||||
expect(
|
||||
shouldEnableAppendOnlyContext("auto", {
|
||||
...GENERIC_PROXY,
|
||||
compat: { supportsStore: true },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("auto remains off for unknown providers without prefix-cache signals", () => {
|
||||
expect(shouldEnableAppendOnlyContext("auto", GENERIC_PROXY)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user