mirror of
https://github.com/Narcooo/inkos.git
synced 2026-08-28 14:57:58 +08:00
fix(release): repair npm manifests and MiniMax thinking defaults
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## v1.6.3
|
||||
|
||||
### Hotfix
|
||||
|
||||
- 修复 `@actalk/inkos@1.6.2` / `@actalk/inkos-studio@1.6.2` 发布到 npm 时 registry manifest 泄漏 `workspace:*` 的问题;Windows / npm 全局升级请直接安装 `1.6.3` 或更新到 `latest`
|
||||
- 发布校验现在会拒绝 publishable manifest 中的 `workspace:` 依赖,避免同类安装错误复发
|
||||
- MiniMax 官方 OpenAI-compatible 接入新增 `MiniMax-M3` 模型卡,并对 `MiniMax-M3*` 默认发送 `thinking: { "type": "disabled" }`,减少接口默认返回 thinking 内容的问题
|
||||
|
||||
## v1.6.2
|
||||
|
||||
### Release Focus
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ Keep commits atomic — one logical change per commit. Split new files, interfac
|
||||
- Immutable patterns: `{ ...obj, key: value }` over mutation
|
||||
- Functions < 50 lines, files < 800 lines
|
||||
- Errors must surface, not be swallowed (`catch { }` without re-throw needs a comment)
|
||||
- `workspace:*` stays in source `package.json` — the CI pipeline handles version replacement at publish time
|
||||
- Publishable package manifests must use registry-installable internal versions, not `workspace:*`; `pnpm` links local packages through the workspace config during development.
|
||||
|
||||
## Adding a CLI Command
|
||||
|
||||
|
||||
@@ -165,6 +165,8 @@ Open Studio, then go to **Model Settings**:
|
||||
|
||||
Studio uses project service settings and `.inkos/secrets.json`. It may show env-detection hints, but env files do not override the Studio-selected service/model/base URL/API key.
|
||||
|
||||
MiniMax uses the official OpenAI-compatible `/v1/chat/completions` endpoint. InkOS disables returned thinking by default for `MiniMax-M3*`; M2.x thinking cannot be disabled by the upstream service.
|
||||
|
||||
**Option 2: CLI / daemon / deployment env config**
|
||||
|
||||
```bash
|
||||
|
||||
@@ -130,6 +130,8 @@ Studio を開き、**モデル設定**へ進みます:
|
||||
|
||||
Studio はプロジェクトのサービス設定と `.inkos/secrets.json` を使います。env が検出されてもヒントとして表示するだけで、Studio で選んだ service / model / base URL / API Key を上書きしません。
|
||||
|
||||
MiniMax は公式 OpenAI-compatible `/v1/chat/completions` エンドポイントを使用します。InkOS は `MiniMax-M3*` の thinking 返却をデフォルトで無効化します。M2.x の thinking は上流サービス側の制限により無効化できません。
|
||||
|
||||
**方法2:CLI / daemon / デプロイ環境の env 設定**
|
||||
|
||||
```bash
|
||||
|
||||
@@ -273,7 +273,7 @@ inkos doctor
|
||||
| `legacy-env` | 旧 env 模式:兼容老项目的纯 `.env` 配置 |
|
||||
|
||||
|
||||
如果服务测试失败,优先检查服务商、模型和协议是否匹配。Google Gemini 的 AI Studio API Key 可用于 Gemini OpenAI-compatible endpoint;InkOS 会自动禁用 Google 不支持的 OpenAI `store` 参数。MiniMax / MiniMax CodingPlan 默认走官方 OpenAI-compatible `/v1/chat/completions`,并优先使用可工作的非流式 transport,避免流式返回 usage 但无正文的问题。
|
||||
如果服务测试失败,优先检查服务商、模型和协议是否匹配。Google Gemini 的 AI Studio API Key 可用于 Gemini OpenAI-compatible endpoint;InkOS 会自动禁用 Google 不支持的 OpenAI `store` 参数。MiniMax 默认走官方 OpenAI-compatible `/v1/chat/completions`,并优先使用可工作的非流式 transport,避免流式返回 usage 但无正文的问题;`MiniMax-M3*` 会默认关闭 thinking 返回,M2.x thinking 由上游限制无法关闭。
|
||||
|
||||
### LLM 配置更新
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "inkos",
|
||||
"version": "1.6.2",
|
||||
"version": "1.6.3",
|
||||
"private": true,
|
||||
"description": "Story Creation AI Agent for long-form and short fiction, scripts, interactive games, and IP content",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actalk/inkos",
|
||||
"version": "1.6.2",
|
||||
"version": "1.6.3",
|
||||
"description": "Story Creation AI Agent CLI for long-form fiction, InkOS Short, fan works, continuations, covers, and interactive worlds.",
|
||||
"keywords": [
|
||||
"story-creation-ai-agent",
|
||||
@@ -49,8 +49,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actalk/inkos-core": "workspace:*",
|
||||
"@actalk/inkos-studio": "workspace:*",
|
||||
"@actalk/inkos-core": "1.6.3",
|
||||
"@actalk/inkos-studio": "1.6.3",
|
||||
"commander": "^13.0.0",
|
||||
"dotenv": "^16.4.0",
|
||||
"epub-gen-memory": "^1.0.10",
|
||||
|
||||
@@ -117,11 +117,13 @@ describe.sequential("publish packaging", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps source CLI dependencies linked through the workspace protocol", async () => {
|
||||
it("keeps source publish dependencies registry-installable", async () => {
|
||||
const cliPackageJson = await sourceCliPackageJsonPromise;
|
||||
const studioPackageJson = await sourceStudioPackageJsonPromise;
|
||||
|
||||
expect(cliPackageJson.dependencies["@actalk/inkos-core"]).toBe("workspace:*");
|
||||
expect(cliPackageJson.dependencies["@actalk/inkos-studio"]).toBe("workspace:*");
|
||||
expect(cliPackageJson.dependencies["@actalk/inkos-core"]).not.toMatch(/^workspace:/);
|
||||
expect(cliPackageJson.dependencies["@actalk/inkos-studio"]).not.toMatch(/^workspace:/);
|
||||
expect(studioPackageJson.dependencies["@actalk/inkos-core"]).not.toMatch(/^workspace:/);
|
||||
});
|
||||
|
||||
it("verifies publishable manifests before npm publish runs", async () => {
|
||||
@@ -138,7 +140,7 @@ describe.sequential("publish packaging", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("allows source workspace protocol manifests when they normalize cleanly for publish", async () => {
|
||||
it("rejects workspace protocol in source publish manifests", async () => {
|
||||
const tempRoot = await mkdtemp(join(tmpdir(), "inkos-publish-verify-pass-"));
|
||||
const tempPackagesDir = join(tempRoot, "packages");
|
||||
const tempCoreDir = join(tempPackagesDir, "core");
|
||||
@@ -182,7 +184,7 @@ describe.sequential("publish packaging", () => {
|
||||
encoding: "utf-8",
|
||||
stdio: "pipe",
|
||||
},
|
||||
)).not.toThrow();
|
||||
)).toThrow(/workspace protocol is not allowed/);
|
||||
} finally {
|
||||
await rm(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
@@ -231,7 +233,7 @@ describe.sequential("publish packaging", () => {
|
||||
encoding: "utf-8",
|
||||
stdio: "pipe",
|
||||
},
|
||||
)).toThrow(/normalizes to 0\.5\.0, expected 0\.5\.1/);
|
||||
)).toThrow(/workspace protocol is not allowed/);
|
||||
} finally {
|
||||
await rm(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actalk/inkos-core",
|
||||
"version": "1.6.2",
|
||||
"version": "1.6.3",
|
||||
"description": "InkOS core engine for story creation agents: governed context, writing, audit, revision, covers, and interactive worlds",
|
||||
"keywords": [
|
||||
"story-creation-ai-agent",
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { chatCompletion, createLLMClient } from "../llm/provider.js";
|
||||
|
||||
const { fetchCalls } = vi.hoisted(() => ({
|
||||
fetchCalls: [] as Array<{ url: string; init: RequestInit; body: Record<string, unknown> }>,
|
||||
}));
|
||||
|
||||
vi.mock("../utils/proxy-fetch.js", () => ({
|
||||
fetchWithProxy: vi.fn(async (url: string, init: RequestInit) => {
|
||||
fetchCalls.push({
|
||||
url,
|
||||
init,
|
||||
body: JSON.parse(String(init.body ?? "{}")),
|
||||
});
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
choices: [{ message: { content: "ok" } }],
|
||||
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
|
||||
}),
|
||||
} as Response;
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@mariozechner/pi-ai", () => ({
|
||||
completeSimple: vi.fn(async () => {
|
||||
throw new Error("MiniMax OpenAI-compatible requests must use InkOS native transport");
|
||||
}),
|
||||
streamSimple: vi.fn(async function* () {
|
||||
throw new Error("MiniMax OpenAI-compatible requests must use InkOS native transport");
|
||||
}),
|
||||
}));
|
||||
|
||||
function minimaxClient(model: string) {
|
||||
return createLLMClient({
|
||||
provider: "openai",
|
||||
service: "minimax",
|
||||
model,
|
||||
apiKey: "sk-test",
|
||||
apiFormat: "chat",
|
||||
stream: false,
|
||||
temperature: 0.9,
|
||||
thinkingBudget: 0,
|
||||
extra: {},
|
||||
} as never);
|
||||
}
|
||||
|
||||
describe("MiniMax thinking defaults", () => {
|
||||
it("disables MiniMax-M3 thinking by default on the OpenAI-compatible endpoint", async () => {
|
||||
fetchCalls.length = 0;
|
||||
const client = minimaxClient("MiniMax-M3");
|
||||
|
||||
const result = await chatCompletion(client, "MiniMax-M3", [
|
||||
{ role: "user", content: "hi" },
|
||||
], { retry: false });
|
||||
|
||||
expect(result.content).toBe("ok");
|
||||
expect(fetchCalls).toHaveLength(1);
|
||||
expect(fetchCalls[0]!.url).toBe("https://api.minimaxi.com/v1/chat/completions");
|
||||
expect(fetchCalls[0]!.body).toMatchObject({
|
||||
model: "MiniMax-M3",
|
||||
thinking: { type: "disabled" },
|
||||
});
|
||||
});
|
||||
|
||||
it("does not send unsupported thinking controls to MiniMax-M2.x models", async () => {
|
||||
fetchCalls.length = 0;
|
||||
const client = minimaxClient("MiniMax-M2.7");
|
||||
|
||||
await chatCompletion(client, "MiniMax-M2.7", [
|
||||
{ role: "user", content: "hi" },
|
||||
], { retry: false });
|
||||
|
||||
expect(fetchCalls).toHaveLength(1);
|
||||
expect(fetchCalls[0]!.body).not.toHaveProperty("thinking");
|
||||
});
|
||||
});
|
||||
@@ -31,10 +31,11 @@ describe("service-presets regression", () => {
|
||||
expect(preset!.api).toBe("openai-completions");
|
||||
});
|
||||
|
||||
it("has knownModels with all 7 MiniMax models", () => {
|
||||
it("has knownModels with current MiniMax chat models", () => {
|
||||
const preset = resolveServicePreset("minimax");
|
||||
expect(preset!.knownModels).toBeDefined();
|
||||
expect(preset!.knownModels).toHaveLength(7);
|
||||
expect(preset!.knownModels).toHaveLength(8);
|
||||
expect(preset!.knownModels).toContain("MiniMax-M3");
|
||||
expect(preset!.knownModels).toContain("MiniMax-M2.7");
|
||||
expect(preset!.knownModels).toContain("MiniMax-M2.7-highspeed");
|
||||
expect(preset!.knownModels).toContain("MiniMax-M2.5");
|
||||
@@ -62,6 +63,7 @@ describe("service-presets regression", () => {
|
||||
it("returns provider bank models for minimax (B8 升级:provider.models 替代 preset.knownModels)", async () => {
|
||||
const models = await listModelsForService("minimax");
|
||||
expect(models.length).toBeGreaterThanOrEqual(7);
|
||||
expect(models.some((m) => m.id === "MiniMax-M3")).toBe(true);
|
||||
expect(models.some((m) => m.id === "MiniMax-M2.7")).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -616,6 +616,9 @@ async function withTransientLLMRetry<T>(
|
||||
}
|
||||
|
||||
function shouldUseNativeCustomTransport(client: LLMClient): boolean {
|
||||
if (client.service === "minimax" && client.provider === "openai") {
|
||||
return true;
|
||||
}
|
||||
if (client.service === "kkaiapi" && client.provider === "openai") {
|
||||
return true;
|
||||
}
|
||||
@@ -650,6 +653,16 @@ function buildCustomHeaders(client: LLMClient): Record<string, string> {
|
||||
}) ?? { "Content-Type": "application/json" };
|
||||
}
|
||||
|
||||
function defaultOpenAIChatExtra(client: LLMClient, model: string): Record<string, unknown> {
|
||||
if (
|
||||
client.service === "minimax"
|
||||
&& /^minimax-m3(?:$|[-_.])/i.test(model)
|
||||
) {
|
||||
return { thinking: { type: "disabled" } };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function sanitizeHeaderApiKey(apiKey: string | undefined): string {
|
||||
const trimmed = apiKey?.trim() ?? "";
|
||||
if (!trimmed) return "";
|
||||
@@ -1041,6 +1054,7 @@ async function chatCompletionViaCustomOpenAICompatible(
|
||||
stream: client.stream,
|
||||
temperature: resolved.temperature,
|
||||
max_tokens: resolved.maxTokens,
|
||||
...defaultOpenAIChatExtra(client, model),
|
||||
...extra,
|
||||
};
|
||||
if (client.stream) {
|
||||
|
||||
@@ -24,6 +24,7 @@ export const MINIMAX: InkosEndpoint = {
|
||||
defaultTemperature: 0.9,
|
||||
writingTemperature: 0.9,
|
||||
models: [
|
||||
{ id: "MiniMax-M3", maxOutput: 131072, contextWindowTokens: 1_000_000, enabled: true, releasedAt: "2026-07-01" },
|
||||
{ id: "MiniMax-M2.7", maxOutput: 131072, contextWindowTokens: 204800, enabled: true, releasedAt: "2026-03-18" },
|
||||
{ id: "MiniMax-M2.7-highspeed", maxOutput: 131072, contextWindowTokens: 204800, releasedAt: "2026-03-18" },
|
||||
{ id: "MiniMax-M2.5", maxOutput: 131072, contextWindowTokens: 204800, releasedAt: "2026-02-12" },
|
||||
|
||||
@@ -29,7 +29,7 @@ export const SERVICE_PRESETS: Record<string, ServicePreset> = {
|
||||
temperatureRange: [0, 2],
|
||||
defaultTemperature: 0.9,
|
||||
writingTemperature: 0.9,
|
||||
knownModels: ["MiniMax-M2.7", "MiniMax-M2.7-highspeed", "MiniMax-M2.5", "MiniMax-M2.5-highspeed", "MiniMax-M2.1", "MiniMax-M2.1-highspeed", "MiniMax-M2"],
|
||||
knownModels: ["MiniMax-M3", "MiniMax-M2.7", "MiniMax-M2.7-highspeed", "MiniMax-M2.5", "MiniMax-M2.5-highspeed", "MiniMax-M2.1", "MiniMax-M2.1-highspeed", "MiniMax-M2"],
|
||||
},
|
||||
bailian: {
|
||||
providerFamily: "anthropic",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actalk/inkos-studio",
|
||||
"version": "1.6.2",
|
||||
"version": "1.6.3",
|
||||
"description": "InkOS Studio — web workbench for story creation, InkOS Short, covers, and open-world interactive play",
|
||||
"type": "module",
|
||||
"main": "dist/api/index.js",
|
||||
@@ -31,7 +31,7 @@
|
||||
"typecheck": "tsc --noEmit && tsc -p tsconfig.server.json --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actalk/inkos-core": "workspace:*",
|
||||
"@actalk/inkos-core": "1.6.3",
|
||||
"@base-ui/react": "^1.3.0",
|
||||
"@fontsource-variable/geist": "^5.2.8",
|
||||
"@hono/node-server": "^1.13.0",
|
||||
|
||||
Generated
+3
-3
@@ -15,10 +15,10 @@ importers:
|
||||
packages/cli:
|
||||
dependencies:
|
||||
'@actalk/inkos-core':
|
||||
specifier: workspace:*
|
||||
specifier: 1.6.3
|
||||
version: link:../core
|
||||
'@actalk/inkos-studio':
|
||||
specifier: workspace:*
|
||||
specifier: 1.6.3
|
||||
version: link:../studio
|
||||
commander:
|
||||
specifier: ^13.0.0
|
||||
@@ -107,7 +107,7 @@ importers:
|
||||
packages/studio:
|
||||
dependencies:
|
||||
'@actalk/inkos-core':
|
||||
specifier: workspace:*
|
||||
specifier: 1.6.3
|
||||
version: link:../core
|
||||
'@base-ui/react':
|
||||
specifier: ^1.3.0
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
/**
|
||||
* Verify that source manifests are publishable once prepack normalization runs.
|
||||
* Verify that source manifests are publishable as registry manifests.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/verify-no-workspace-protocol.mjs packages/cli packages/core
|
||||
* node ../../scripts/verify-no-workspace-protocol.mjs .
|
||||
*
|
||||
* This script is safe to run on source manifests before prepack.
|
||||
*
|
||||
* Checks two invariants before publish:
|
||||
* 1. workspace:* / workspace:^ / workspace:~ references can be normalized to real versions
|
||||
* 2. non-workspace internal dependencies already point at the current workspace version
|
||||
* 1. dependencies / optionalDependencies / peerDependencies contain no workspace: specifiers
|
||||
* 2. internal workspace dependencies point at the current workspace version
|
||||
*/
|
||||
|
||||
import { access, readdir, readFile } from "node:fs/promises";
|
||||
@@ -64,14 +62,6 @@ async function loadWorkspaceVersions(workspaceRoot) {
|
||||
return versions;
|
||||
}
|
||||
|
||||
function normalizeWorkspaceSpecifier(specifier, version) {
|
||||
const value = specifier.slice("workspace:".length);
|
||||
if (value === "*" || value === "") return version;
|
||||
if (value === "^") return `^${version}`;
|
||||
if (value === "~") return `~${version}`;
|
||||
return value;
|
||||
}
|
||||
|
||||
let failed = false;
|
||||
const workspaceRoot = await findWorkspaceRoot(process.cwd());
|
||||
const workspaceVersions = await loadWorkspaceVersions(workspaceRoot);
|
||||
@@ -93,25 +83,11 @@ for (const dirArg of dirs) {
|
||||
}
|
||||
|
||||
if (specifier.startsWith("workspace:")) {
|
||||
if (!workspaceVersion) {
|
||||
process.stderr.write(`FAIL: ${dir} — ${field}.${name}: ${specifier} (workspace package not found)\n`);
|
||||
dirFailed = true;
|
||||
failed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalized = normalizeWorkspaceSpecifier(specifier, workspaceVersion);
|
||||
if (
|
||||
normalized !== workspaceVersion
|
||||
&& normalized !== `^${workspaceVersion}`
|
||||
&& normalized !== `~${workspaceVersion}`
|
||||
) {
|
||||
process.stderr.write(
|
||||
`FAIL: ${dir} — ${field}.${name}: ${specifier} normalizes to ${normalized}, expected ${workspaceVersion}, ^${workspaceVersion}, or ~${workspaceVersion}\n`,
|
||||
);
|
||||
dirFailed = true;
|
||||
failed = true;
|
||||
}
|
||||
process.stderr.write(
|
||||
`FAIL: ${dir} — ${field}.${name}: ${specifier} (workspace protocol is not allowed in publish manifests)\n`,
|
||||
);
|
||||
dirFailed = true;
|
||||
failed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user