mirror of
https://fastgit.cc/https://github.com/anomalyco/opencode
synced 2026-04-20 12:51:13 +08:00
fix: enable thinking for zhipuai-coding-plan & prevent Korean IME truncation (#22041)
Co-authored-by: claudianus <claudianus@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -589,6 +589,13 @@ export function Prompt(props: PromptProps) {
|
|||||||
])
|
])
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
|
// IME: double-defer may fire before onContentChange flushes the last
|
||||||
|
// composed character (e.g. Korean hangul) to the store, so read
|
||||||
|
// plainText directly and sync before any downstream reads.
|
||||||
|
if (input && !input.isDestroyed && input.plainText !== store.prompt.input) {
|
||||||
|
setStore("prompt", "input", input.plainText)
|
||||||
|
syncExtmarksWithPromptParts()
|
||||||
|
}
|
||||||
if (props.disabled) return
|
if (props.disabled) return
|
||||||
if (autocomplete?.visible) return
|
if (autocomplete?.visible) return
|
||||||
if (!store.prompt.input) return
|
if (!store.prompt.input) return
|
||||||
@@ -994,7 +1001,11 @@ export function Prompt(props: PromptProps) {
|
|||||||
input.cursorOffset = input.plainText.length
|
input.cursorOffset = input.plainText.length
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSubmit={submit}
|
onSubmit={() => {
|
||||||
|
// IME: double-defer so the last composed character (e.g. Korean
|
||||||
|
// hangul) is flushed to plainText before we read it for submission.
|
||||||
|
setTimeout(() => setTimeout(() => submit(), 0), 0)
|
||||||
|
}}
|
||||||
onPaste={async (event: PasteEvent) => {
|
onPaste={async (event: PasteEvent) => {
|
||||||
if (props.disabled) {
|
if (props.disabled) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -774,7 +774,10 @@ export namespace ProviderTransform {
|
|||||||
result["chat_template_args"] = { enable_thinking: true }
|
result["chat_template_args"] = { enable_thinking: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (["zai", "zhipuai"].includes(input.model.providerID) && input.model.api.npm === "@ai-sdk/openai-compatible") {
|
if (
|
||||||
|
["zai", "zhipuai"].some((id) => input.model.providerID.includes(id)) &&
|
||||||
|
input.model.api.npm === "@ai-sdk/openai-compatible"
|
||||||
|
) {
|
||||||
result["thinking"] = {
|
result["thinking"] = {
|
||||||
type: "enabled",
|
type: "enabled",
|
||||||
clear_thinking: false,
|
clear_thinking: false,
|
||||||
|
|||||||
@@ -104,6 +104,58 @@ describe("ProviderTransform.options - setCacheKey", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("ProviderTransform.options - zai/zhipuai thinking", () => {
|
||||||
|
const sessionID = "test-session-123"
|
||||||
|
|
||||||
|
const createModel = (providerID: string) =>
|
||||||
|
({
|
||||||
|
id: `${providerID}/glm-4.6`,
|
||||||
|
providerID,
|
||||||
|
api: {
|
||||||
|
id: "glm-4.6",
|
||||||
|
url: "https://open.bigmodel.cn/api/paas/v4",
|
||||||
|
npm: "@ai-sdk/openai-compatible",
|
||||||
|
},
|
||||||
|
name: "GLM 4.6",
|
||||||
|
capabilities: {
|
||||||
|
temperature: true,
|
||||||
|
reasoning: true,
|
||||||
|
attachment: true,
|
||||||
|
toolcall: true,
|
||||||
|
input: { text: true, audio: false, image: true, video: false, pdf: true },
|
||||||
|
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||||
|
interleaved: false,
|
||||||
|
},
|
||||||
|
cost: {
|
||||||
|
input: 0.001,
|
||||||
|
output: 0.002,
|
||||||
|
cache: { read: 0.0001, write: 0.0002 },
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
context: 128000,
|
||||||
|
output: 8192,
|
||||||
|
},
|
||||||
|
status: "active",
|
||||||
|
options: {},
|
||||||
|
headers: {},
|
||||||
|
}) as any
|
||||||
|
|
||||||
|
for (const providerID of ["zai-coding-plan", "zai", "zhipuai-coding-plan", "zhipuai"]) {
|
||||||
|
test(`${providerID} should set thinking cfg`, () => {
|
||||||
|
const result = ProviderTransform.options({
|
||||||
|
model: createModel(providerID),
|
||||||
|
sessionID,
|
||||||
|
providerOptions: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.thinking).toEqual({
|
||||||
|
type: "enabled",
|
||||||
|
clear_thinking: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
describe("ProviderTransform.options - google thinkingConfig gating", () => {
|
describe("ProviderTransform.options - google thinkingConfig gating", () => {
|
||||||
const sessionID = "test-session-123"
|
const sessionID = "test-session-123"
|
||||||
|
|
||||||
|
|||||||
120
patches/install-korean-ime-fix.sh
Executable file
120
patches/install-korean-ime-fix.sh
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# opencode Korean IME Fix Installer
|
||||||
|
# https://github.com/anomalyco/opencode/issues/14371
|
||||||
|
#
|
||||||
|
# Patches opencode to prevent Korean (and other CJK) IME last character
|
||||||
|
# truncation when pressing Enter in Kitty and other terminals.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/claudianus/opencode/fix-zhipuai-coding-plan-thinking/patches/install-korean-ime-fix.sh | bash
|
||||||
|
# # or from a cloned repo:
|
||||||
|
# ./patches/install-korean-ime-fix.sh
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
ORANGE='\033[38;5;214m'
|
||||||
|
MUTED='\033[0;2m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
OPENCODE_DIR="${OPENCODE_DIR:-$HOME/.opencode}"
|
||||||
|
OPENCODE_SRC="${OPENCODE_SRC:-$HOME/.opencode-src}"
|
||||||
|
FORK_REPO="${FORK_REPO:-https://github.com/claudianus/opencode.git}"
|
||||||
|
FORK_BRANCH="${FORK_BRANCH:-fix-zhipuai-coding-plan-thinking}"
|
||||||
|
|
||||||
|
info() { echo -e "${MUTED}$*${NC}"; }
|
||||||
|
warn() { echo -e "${ORANGE}$*${NC}"; }
|
||||||
|
err() { echo -e "${RED}$*${NC}" >&2; }
|
||||||
|
ok() { echo -e "${GREEN}$*${NC}"; }
|
||||||
|
|
||||||
|
need() {
|
||||||
|
if ! command -v "$1" >/dev/null 2>&1; then
|
||||||
|
err "Error: $1 is required but not installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
need git
|
||||||
|
need bun
|
||||||
|
|
||||||
|
# ── 1. Clone or update fork ────────────────────────────────────────────
|
||||||
|
if [ -d "$OPENCODE_SRC/.git" ]; then
|
||||||
|
info "Updating existing source at $OPENCODE_SRC ..."
|
||||||
|
git -C "$OPENCODE_SRC" fetch origin "$FORK_BRANCH"
|
||||||
|
git -C "$OPENCODE_SRC" checkout "$FORK_BRANCH"
|
||||||
|
git -C "$OPENCODE_SRC" reset --hard "origin/$FORK_BRANCH"
|
||||||
|
else
|
||||||
|
info "Cloning fork (shallow) to $OPENCODE_SRC ..."
|
||||||
|
git clone --depth 1 --branch "$FORK_BRANCH" "$FORK_REPO" "$OPENCODE_SRC"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. Verify the IME fix is present in source ────────────────────────
|
||||||
|
PROMPT_FILE="$OPENCODE_SRC/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx"
|
||||||
|
if [ ! -f "$PROMPT_FILE" ]; then
|
||||||
|
err "Prompt file not found: $PROMPT_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "setTimeout(() => setTimeout" "$PROMPT_FILE"; then
|
||||||
|
ok "IME fix already present in source."
|
||||||
|
else
|
||||||
|
warn "IME fix not found. Applying patch ..."
|
||||||
|
# Apply the fix: replace onSubmit={submit} with double-deferred version
|
||||||
|
sed -i 's|onSubmit={submit}|onSubmit={() => {\n // IME: double-defer so the last composed character (e.g. Korean\n // hangul) is flushed to plainText before we read it for submission.\n setTimeout(() => setTimeout(() => submit(), 0), 0)\n }}|' "$PROMPT_FILE"
|
||||||
|
if grep -q "setTimeout(() => setTimeout" "$PROMPT_FILE"; then
|
||||||
|
ok "Patch applied."
|
||||||
|
else
|
||||||
|
err "Failed to apply patch. The source may have changed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3. Install dependencies ────────────────────────────────────────────
|
||||||
|
info "Installing dependencies (this may take a minute) ..."
|
||||||
|
cd "$OPENCODE_SRC"
|
||||||
|
bun install --frozen-lockfile 2>/dev/null || bun install
|
||||||
|
|
||||||
|
# ── 4. Build (current platform only) ──────────────────────────────────
|
||||||
|
info "Building opencode for current platform ..."
|
||||||
|
cd "$OPENCODE_SRC/packages/opencode"
|
||||||
|
bun run build --single
|
||||||
|
|
||||||
|
# ── 5. Install binary ──────────────────────────────────────────────────
|
||||||
|
mkdir -p "$OPENCODE_DIR/bin"
|
||||||
|
|
||||||
|
PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
[ "$ARCH" = "aarch64" ] && ARCH="arm64"
|
||||||
|
[ "$ARCH" = "x86_64" ] && ARCH="x64"
|
||||||
|
[ "$PLATFORM" = "darwin" ] && true
|
||||||
|
[ "$PLATFORM" = "linux" ] && true
|
||||||
|
|
||||||
|
BUILT_BINARY="$OPENCODE_SRC/packages/opencode/dist/opencode-${PLATFORM}-${ARCH}/bin/opencode"
|
||||||
|
|
||||||
|
if [ ! -f "$BUILT_BINARY" ]; then
|
||||||
|
BUILT_BINARY=$(find "$OPENCODE_SRC/packages/opencode/dist" -name "opencode" -type f -executable 2>/dev/null | head -1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$BUILT_BINARY" ]; then
|
||||||
|
if [ -f "$OPENCODE_DIR/bin/opencode" ]; then
|
||||||
|
cp "$OPENCODE_DIR/bin/opencode" "$OPENCODE_DIR/bin/opencode.bak.$(date +%Y%m%d%H%M%S)"
|
||||||
|
fi
|
||||||
|
cp "$BUILT_BINARY" "$OPENCODE_DIR/bin/opencode"
|
||||||
|
chmod +x "$OPENCODE_DIR/bin/opencode"
|
||||||
|
ok "Installed to $OPENCODE_DIR/bin/opencode"
|
||||||
|
else
|
||||||
|
err "Build failed - binary not found in dist/"
|
||||||
|
info "Try running manually:"
|
||||||
|
echo " cd $OPENCODE_SRC/packages/opencode && bun run build --single"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
ok "Done! Korean IME fix is now active."
|
||||||
|
echo ""
|
||||||
|
info "To uninstall and revert to the official release:"
|
||||||
|
echo " curl -fsSL https://opencode.ai/install | bash"
|
||||||
|
echo ""
|
||||||
|
info "To update (re-pull and rebuild):"
|
||||||
|
echo " $0"
|
||||||
Reference in New Issue
Block a user