diff --git a/.env.example b/.env.example index 2eec4b42..f883865e 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,15 @@ # ============================================================ # MiniMax(直连 Anthropic 兼容接口) +# 海外用户: ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic +# 国内用户: ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic +# 可用模型: MiniMax-M2.7(默认)、MiniMax-M2.7-highspeed(更快) # ============================================================ -# ANTHROPIC_AUTH_TOKEN=your_token_here -# ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic -# ANTHROPIC_MODEL=MiniMax-M2.7-highspeed -# ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed +# ANTHROPIC_AUTH_TOKEN=your_minimax_api_key_here +# ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic +# ANTHROPIC_MODEL=MiniMax-M2.7 +# ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7 # ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed -# ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed +# ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7 # API_TIMEOUT_MS=3000000 # ============================================================ diff --git a/docs/en/guide/third-party-models.md b/docs/en/guide/third-party-models.md index 66416c50..7cb3b3fb 100644 --- a/docs/en/guide/third-party-models.md +++ b/docs/en/guide/third-party-models.md @@ -172,18 +172,28 @@ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 ### MiniMax (pre-configured in .env.example) +MiniMax provides an Anthropic-compatible API endpoint and can be connected directly without any proxy. Available models: + +| Model | Description | +|-------|-------------| +| `MiniMax-M2.7` | Default recommended, excellent overall performance | +| `MiniMax-M2.7-highspeed` | Faster responses, suitable for latency-sensitive use cases | + ```env -ANTHROPIC_AUTH_TOKEN=your_token_here -ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic -ANTHROPIC_MODEL=MiniMax-M2.7-highspeed -ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed +ANTHROPIC_AUTH_TOKEN=your_minimax_api_key_here +# International users: api.minimax.io; China users may use api.minimaxi.com +ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic +ANTHROPIC_MODEL=MiniMax-M2.7 +ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7 ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed -ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed +ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7 API_TIMEOUT_MS=3000000 DISABLE_TELEMETRY=1 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 ``` +> **Get API Key**: Visit [MiniMax Open Platform](https://platform.minimax.io) to register and obtain an API key. + --- ## Option 3: Other Proxy Tools diff --git a/docs/guide/third-party-models.md b/docs/guide/third-party-models.md index 1bc746a6..93b6ccdc 100644 --- a/docs/guide/third-party-models.md +++ b/docs/guide/third-party-models.md @@ -172,18 +172,28 @@ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 ### MiniMax(已在 .env.example 中配置) +MiniMax 提供 Anthropic 兼容接口,支持直接接入,无需代理。可用模型: + +| 模型 | 说明 | +|------|------| +| `MiniMax-M2.7` | 默认推荐,综合性能优秀 | +| `MiniMax-M2.7-highspeed` | 响应更快,适合对速度有要求的场景 | + ```env -ANTHROPIC_AUTH_TOKEN=your_token_here -ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic -ANTHROPIC_MODEL=MiniMax-M2.7-highspeed -ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed +ANTHROPIC_AUTH_TOKEN=your_minimax_api_key_here +# 海外用户使用 api.minimax.io,国内用户可改为 api.minimaxi.com +ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic +ANTHROPIC_MODEL=MiniMax-M2.7 +ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7 ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed -ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed +ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7 API_TIMEOUT_MS=3000000 DISABLE_TELEMETRY=1 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 ``` +> **获取 API Key**:访问 [MiniMax 开放平台](https://platform.minimax.io) 注册并获取 API Key。 + --- ## 方式三:其他代理工具 diff --git a/src/buddy/companion.ts b/src/buddy/companion.ts index 09c38386..52f3359f 100644 --- a/src/buddy/companion.ts +++ b/src/buddy/companion.ts @@ -7,6 +7,7 @@ import { RARITIES, RARITY_WEIGHTS, type Rarity, + type Species, SPECIES, STAT_NAMES, type StatName, @@ -121,13 +122,35 @@ export function companionUserId(): string { return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon' } -// Regenerate bones from userId, merge with stored soul. Bones never persist -// so species renames and SPECIES-array edits can't break stored companions, -// and editing config.companion can't fake a rarity. +function storedAppearanceSeed(stored: { + appearanceSeed?: string + hatchedAt: number + name: string + personality: string +}): string { + return ( + stored.appearanceSeed ?? + `legacy:${stored.hatchedAt}:${stored.name}:${stored.personality}` + ) +} + +function applyStoredOverrides( + bones: CompanionBones, + stored: { speciesOverride?: Species }, +): CompanionBones { + return stored.speciesOverride + ? { ...bones, species: stored.speciesOverride } + : bones +} + +// Regenerate bones from the stored hatch seed, merge with the saved soul. +// Old configs without a seed get a deterministic legacy fallback so they stop +// changing every render without requiring users to re-hatch. export function getCompanion(): Companion | undefined { const stored = getGlobalConfig().companion if (!stored) return undefined - const { bones } = roll(companionUserId()) + const { bones: rolledBones } = rollWithSeed(storedAppearanceSeed(stored)) + const bones = applyStoredOverrides(rolledBones, stored) // bones last so stale bones fields in old-format configs get overridden return { ...stored, ...bones } } diff --git a/src/buddy/types.ts b/src/buddy/types.ts index 8f1c82aa..8f17b96f 100644 --- a/src/buddy/types.ts +++ b/src/buddy/types.ts @@ -118,10 +118,13 @@ export type Companion = CompanionBones & hatchedAt: number } -// What actually persists in config. Bones are regenerated from hash(userId) -// on every read so species renames don't break stored companions and users -// can't edit their way to a legendary. -export type StoredCompanion = CompanionSoul & { hatchedAt: number } +// What actually persists in config. The hatch seed locks in a companion's +// bones so the sprite/species stop changing between renders and restarts. +export type StoredCompanion = CompanionSoul & { + hatchedAt: number + appearanceSeed?: string + speciesOverride?: Species +} export const RARITY_WEIGHTS = { common: 60, diff --git a/src/commands/buddy/buddy.tsx b/src/commands/buddy/buddy.tsx index 56d7e9c9..235ce6c8 100644 --- a/src/commands/buddy/buddy.tsx +++ b/src/commands/buddy/buddy.tsx @@ -4,6 +4,7 @@ import type { LocalJSXCommandCall } from '../../types/command.js' import { getCompanion, roll, + rollWithSeed, companionUserId, } from '../../buddy/companion.js' import { renderSprite } from '../../buddy/sprites.js' @@ -73,8 +74,9 @@ function CompanionCard({ ) return } - // Hatch a new companion with a generated name - const { bones } = roll(companionUserId()) + // Hatch a new companion with a generated name and random seed + const appearanceSeed = `hatch:${Date.now()}:${Math.random().toString(36).slice(2)}` + const { bones } = rollWithSeed(appearanceSeed) const adjectives = [ 'Bright', 'Cozy', 'Swift', 'Calm', 'Wise', 'Bold', 'Fuzzy', 'Lucky', 'Snappy', 'Quirky', @@ -90,6 +92,7 @@ function CompanionCard({ name, personality: `A ${bones.rarity} ${bones.species} who loves debugging and hanging out.`, hatchedAt: Date.now(), + appearanceSeed, } saveGlobalConfig(c => ({ ...c, companion: soul })) onDone(