feat: publish to PyPI + fix installation issues

- Restructure deps: move llama-cpp-python/tree-sitter to optional
  (base install is pure Python, no compiler needed)
- Add __main__.py for pipx/python -m support
- Add GitHub Actions auto-publish on tag push
- Fix install scripts: correct package name, remove Ollama check,
  add pipx priority
- Wrap ast_engine imports in try/except for graceful degradation
- Published v1.0.7 to PyPI: pip install kwcode

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Val-sss
2026-04-30 11:45:56 +08:00
parent a88e0c18b3
commit edd7e5d1ee
7 changed files with 169 additions and 303 deletions

35
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Publish to PyPI
on:
push:
tags:
- "v*"
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build tools
run: pip install build twine
- name: Build package
run: python -m build
- name: Check package
run: twine check dist/*
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: twine upload dist/*

View File

@@ -10,12 +10,22 @@
[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org)
[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Mac%20%7C%20Linux-lightgrey.svg)]()
[![Tests](https://img.shields.io/badge/Tests-311%2F311-brightgreen.svg)]()
[![Version](https://img.shields.io/badge/Version-1.0.3-blue.svg)]()
[![Version](https://img.shields.io/badge/Version-1.0.7-blue.svg)]()
</div>
---
> **v1.0.7 已发布,可测试使用!** 感谢各位试用提报优化建议。安装命令:
>
> ```bash
> pip install kwcode
> # 国内加速
> pip install kwcode -i https://pypi.tuna.tsinghua.edu.cn/simple
> ```
---
## 更新日志
| 日期 | 内容 |

View File

@@ -7,7 +7,7 @@ $ErrorActionPreference = "Continue"
# ── Banner ───────────────────────────────────────────────────
Write-Host ""
Write-Host " ╔══════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host " ║ KwCode 安装程序 v0.4" -ForegroundColor Cyan
Write-Host " ║ KwCode 安装程序 v1.0" -ForegroundColor Cyan
Write-Host " ║ 本地模型 Coding Agent ║" -ForegroundColor Cyan
Write-Host " ╚══════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""
@@ -46,40 +46,35 @@ if (-not $pythonCmd) {
exit 1
}
# ── Step 2: GPU 检查 ─────────────────────────────────────────
Write-Step "检查 GPU..."
$vramMB = 0
try {
$smiOutput = & nvidia-smi --query-gpu=name,memory.total --format=csv,noheader 2>&1
if ($LASTEXITCODE -eq 0 -and $smiOutput -match "(\d+)\s*MiB") {
$vramMB = [int]$Matches[1]
$gpuName = ($smiOutput -split ",")[0].Trim()
Write-Info "GPU: $gpuName VRAM: $($vramMB)MB"
} else {
Write-Warn "未检测到 NVIDIA GPU将使用 CPU 模式(速度较慢)"
}
} catch {
Write-Warn "nvidia-smi 不可用,将使用 CPU 模式"
}
# ── Step 3: pip install kaiwu ────────────────────────────────
# ── Step 2: 安装 KwCode ──────────────────────────────────────
Write-Step "安装 KwCode..."
$installed = $false
# 尝试默认源
Write-Info "尝试默认 pip 源..."
& $pythonCmd -m pip install kaiwu --quiet 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
$installed = $true
Write-Info "安装成功(默认源)"
# 优先 pipx隔离环境不污染系统 Python
if (Get-Command pipx -ErrorAction SilentlyContinue) {
Write-Info "检测到 pipx使用隔离安装..."
& pipx install kwcode 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
$installed = $true
Write-Info "安装成功pipx 隔离环境)"
}
}
# 降级到 pip 默认源
if (-not $installed) {
Write-Info "尝试 pip 安装..."
& $pythonCmd -m pip install kwcode --quiet 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
$installed = $true
Write-Info "安装成功pip 默认源)"
}
}
# 降级到清华镜像
if (-not $installed) {
Write-Warn "默认源安装失败,切换到清华镜像..."
& $pythonCmd -m pip install kaiwu -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn --quiet 2>&1 | Out-Null
Write-Warn "默认源失败,切换到清华镜像..."
& $pythonCmd -m pip install kwcode -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn --quiet 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
$installed = $true
Write-Info "安装成功(清华镜像)"
@@ -88,117 +83,26 @@ if (-not $installed) {
if (-not $installed) {
Write-Err "KwCode 安装失败"
Write-Info "请手动执行: $pythonCmd -m pip install kaiwu"
Write-Info "请手动执行: $pythonCmd -m pip install kwcode"
Write-Info "如果网络慢,加上: -i https://pypi.tuna.tsinghua.edu.cn/simple"
exit 1
}
# ── Step 4: Ollama 检查 ──────────────────────────────────────
Write-Step "检查 Ollama..."
$ollamaOk = $false
try {
$ollamaVer = & ollama --version 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Info "Ollama 已安装: $ollamaVer"
$ollamaOk = $true
}
} catch {}
if (-not $ollamaOk) {
Write-Warn "未检测到 Ollama"
Write-Info ""
Write-Info "Ollama 是运行本地模型的推理引擎,请手动安装:"
Write-Info " 下载地址: https://ollama.com/download"
Write-Info " 安装后重新运行本脚本即可自动拉取模型"
Write-Info ""
}
# ── Step 5: 拉取推荐模型 ─────────────────────────────────────
if ($ollamaOk) {
Write-Step "拉取推荐模型..."
# 根据 VRAM 选择模型
if ($vramMB -ge 16000) {
$model = "qwen3:14b"
Write-Info "VRAM >= 16GB推荐模型: $model"
} elseif ($vramMB -ge 8000) {
$model = "qwen3:8b"
Write-Info "VRAM >= 8GB推荐模型: $model"
} else {
$model = "gemma3:4b"
if ($vramMB -gt 0) {
Write-Info "VRAM < 8GB推荐模型: $model"
} else {
Write-Info "未检测到 GPU使用轻量模型: $model"
}
}
# 检查 ModelScope 镜像(国内加速)
Write-Step "检测模型下载源..."
$hfOk = $false
try {
$r = Invoke-WebRequest -Uri "https://huggingface.co" -TimeoutSec 5 -UseBasicParsing -ErrorAction Stop
$hfOk = $true
Write-Info "HuggingFace 可达,使用默认源"
} catch {
Write-Warn "HuggingFace 不可达,自动切换到 ModelScope"
$env:OLLAMA_MODELS = "https://modelscope.cn/models"
}
Write-Info "正在拉取 $model(首次下载可能需要几分钟)..."
& ollama pull $model
if ($LASTEXITCODE -ne 0) {
Write-Warn "模型拉取失败,请稍后手动执行: ollama pull $model"
} else {
Write-Info "模型 $model 已就绪"
}
}
# ── Step 5.5: SearXNG 搜索服务 ──────────────────────────────
Write-Step "启动搜索服务SearXNG..."
if (Get-Command docker -ErrorAction SilentlyContinue) {
$running = docker ps --format '{{.Names}}' | Where-Object { $_ -eq "kwcode-searxng" }
if ($running) {
Write-Info "SearXNG 已在运行,跳过"
} else {
$exists = docker ps -a --format '{{.Names}}' | Where-Object { $_ -eq "kwcode-searxng" }
if ($exists) {
docker start kwcode-searxng
Write-Info "SearXNG 已重新启动"
} else {
Write-Info "拉取 SearXNG 镜像约150MB..."
docker pull searxng/searxng
docker run -d --name kwcode-searxng --restart always -p 8080:8080 searxng/searxng
Write-Info "等待 SearXNG 启动..."
for ($i = 1; $i -le 15; $i++) {
try {
$null = Invoke-WebRequest -Uri "http://localhost:8080" -TimeoutSec 2 -UseBasicParsing
Write-Info "SearXNG 已就绪http://localhost:8080"
break
} catch { Start-Sleep -Seconds 1 }
}
}
}
# 可选:安装 AST 增强
Write-Info "尝试安装 AST 增强(可选,需要 C 编译器)..."
& $pythonCmd -m pip install "kwcode[ast]" --quiet 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Info "AST 调用图引擎已启用(代码定位更精准)"
} else {
Write-Warn "未检测到 Docker搜索增强将使用 DuckDuckGo 降级方案"
Write-Info "安装 Docker 可获得更好的搜索体验https://docs.docker.com/get-docker/"
Write-Info "AST 增强跳过(无编译器),使用 LLM 定位(功能不受影响)"
}
# ── Step 6: kwcode init ──────────────────────────────────────
Write-Step "初始化 KwCode..."
# ── Step 4: 提示配置模型 ─────────────────────────────────────
Write-Step "模型配置提示..."
Write-Info "KwCode 支持任何 OpenAI 兼容 APIOllama / DeepSeek / Qwen 等)"
Write-Info "安装完成后执行 kwcode init 即可配置 API 地址和模型"
try {
& kwcode init 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Info "KAIWU.md 已初始化"
}
} catch {
Write-Info "跳过初始化(可稍后在项目目录执行 kwcode init"
}
# ── Step 7: kwcode status ────────────────────────────────────
# ── Step 5: 验证安装 ─────────────────────────────────────────
Write-Step "验证安装..."
try {
@@ -220,5 +124,5 @@ Write-Host " 2. kwcode init # 初始化项目记忆" -ForegroundColo
Write-Host " 3. kwcode # 进入交互模式" -ForegroundColor White
Write-Host ' 4. kwcode "修复登录bug" # 直接执行任务' -ForegroundColor White
Write-Host ""
Write-Host " 文档: https://github.com/kaiwu-agent/kaiwu" -ForegroundColor Gray
Write-Host " 文档: https://github.com/val1813/kwcode" -ForegroundColor Gray
Write-Host ""

View File

@@ -1,6 +1,6 @@
#!/bin/sh
# KwCode 安装程序 - Mac/Linux
# 用法: curl -sSL https://raw.githubusercontent.com/kaiwu-agent/kaiwu/main/install.sh | sh
# 用法: curl -sSL https://raw.githubusercontent.com/val1813/kwcode/main/install.sh | sh
# 或: chmod +x install.sh && ./install.sh
set -e
@@ -21,7 +21,7 @@ info() { printf " ${GRAY}%s${NC}\n" "$1"; }
# ── Banner ───────────────────────────────────────────────────
printf "\n"
printf " ${CYAN}╔══════════════════════════════════════╗${NC}\n"
printf " ${CYAN}║ KwCode 安装程序 v0.4${NC}\n"
printf " ${CYAN}║ KwCode 安装程序 v1.0${NC}\n"
printf " ${CYAN}║ 本地模型 Coding Agent ║${NC}\n"
printf " ${CYAN}╚══════════════════════════════════════╝${NC}\n"
printf "\n"
@@ -53,63 +53,33 @@ if [ -z "$PYTHON_CMD" ]; then
exit 1
fi
# ── Step 2: GPU 检查 ─────────────────────────────────────────
step "检查 GPU..."
VRAM_MB=0
OS_TYPE=$(uname -s)
if [ "$OS_TYPE" = "Darwin" ]; then
# macOS - 检查 Apple Silicon 统一内存
chip=$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo "")
if echo "$chip" | grep -qi "apple"; then
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo "0")
VRAM_MB=$((mem_bytes / 1024 / 1024))
info "Apple Silicon: $chip 统一内存: ${VRAM_MB}MB"
info "(统一内存可共享给 GPU实际可用约 75%"
else
# Intel Mac
gpu_info=$(system_profiler SPDisplaysDataType 2>/dev/null | grep -i "vram\|chipset" | head -2 || echo "")
if [ -n "$gpu_info" ]; then
info "GPU: $gpu_info"
else
warn "未检测到独立 GPU将使用 CPU 模式"
fi
fi
else
# Linux - 检查 NVIDIA GPU
if command -v nvidia-smi >/dev/null 2>&1; then
smi_output=$(nvidia-smi --query-gpu=name,memory.total --format=csv,noheader 2>/dev/null || echo "")
if [ -n "$smi_output" ]; then
gpu_name=$(echo "$smi_output" | cut -d, -f1 | xargs)
vram_str=$(echo "$smi_output" | grep -oE '[0-9]+' | tail -1)
if [ -n "$vram_str" ]; then
VRAM_MB=$vram_str
fi
info "GPU: $gpu_name VRAM: ${VRAM_MB}MB"
fi
else
warn "未检测到 NVIDIA GPU (nvidia-smi 不可用)"
info "如有 AMD GPUOllama 也支持 ROCm"
fi
fi
# ── Step 3: pip install kaiwu ────────────────────────────────
# ── Step 2: 安装 KwCode ──────────────────────────────────────
step "安装 KwCode..."
INSTALLED=0
# 尝试默认源
info "尝试默认 pip 源..."
if "$PYTHON_CMD" -m pip install kaiwu --quiet 2>/dev/null; then
INSTALLED=1
info "安装成功(默认源)"
# 优先 pipx隔离环境不污染系统 Python
if command -v pipx >/dev/null 2>&1; then
info "检测到 pipx使用隔离安装..."
if pipx install kwcode 2>/dev/null; then
INSTALLED=1
info "安装成功pipx 隔离环境)"
fi
fi
# 降级到 pip 默认源
if [ "$INSTALLED" -eq 0 ]; then
info "尝试 pip 安装..."
if "$PYTHON_CMD" -m pip install kwcode --quiet 2>/dev/null; then
INSTALLED=1
info "安装成功pip 默认源)"
fi
fi
# 降级到清华镜像
if [ "$INSTALLED" -eq 0 ]; then
warn "默认源安装失败,切换到清华镜像..."
if "$PYTHON_CMD" -m pip install kaiwu \
warn "默认源失败,切换到清华镜像..."
if "$PYTHON_CMD" -m pip install kwcode \
-i https://pypi.tuna.tsinghua.edu.cn/simple \
--trusted-host pypi.tuna.tsinghua.edu.cn \
--quiet 2>/dev/null; then
@@ -120,119 +90,31 @@ fi
if [ "$INSTALLED" -eq 0 ]; then
err "KwCode 安装失败"
info "请手动执行: $PYTHON_CMD -m pip install kaiwu"
info "请手动执行: $PYTHON_CMD -m pip install kwcode"
info "如果网络慢,加上: -i https://pypi.tuna.tsinghua.edu.cn/simple"
exit 1
fi
# ── Step 4: Ollama 检查 ──────────────────────────────────────
step "检查 Ollama..."
OLLAMA_OK=0
if command -v ollama >/dev/null 2>&1; then
ollama_ver=$(ollama --version 2>&1 || echo "unknown")
info "Ollama 已安装: $ollama_ver"
OLLAMA_OK=1
# 可选:安装 AST 增强
info "尝试安装 AST 增强(可选,需要 C 编译器)..."
if "$PYTHON_CMD" -m pip install "kwcode[ast]" --quiet 2>/dev/null; then
info "AST 调用图引擎已启用(代码定位更精准)"
else
warn "未检测到 Ollama"
info ""
info "Ollama 是运行本地模型的推理引擎,请手动安装:"
if [ "$OS_TYPE" = "Darwin" ]; then
info " brew install ollama"
info " 或: https://ollama.com/download"
else
info " curl -fsSL https://ollama.com/install.sh | sh"
fi
info ""
info "安装后重新运行本脚本即可自动拉取模型"
info "AST 增强跳过(无编译器),使用 LLM 定位(功能不受影响)"
fi
# ── Step 5: 拉取推荐模型 ─────────────────────────────────────
if [ "$OLLAMA_OK" -eq 1 ]; then
step "拉取推荐模型..."
# ── Step 3: 提示配置模型 ─────────────────────────────────────
step "模型配置提示..."
info "KwCode 支持任何 OpenAI 兼容 APIOllama / DeepSeek / Qwen 等)"
info "安装完成后执行 kwcode init 即可配置 API 地址和模型"
# 根据 VRAM 选择模型
if [ "$VRAM_MB" -ge 16000 ]; then
MODEL="qwen3:14b"
info "VRAM >= 16GB推荐模型: $MODEL"
elif [ "$VRAM_MB" -ge 8000 ]; then
MODEL="qwen3:8b"
info "VRAM >= 8GB推荐模型: $MODEL"
else
MODEL="gemma3:4b"
if [ "$VRAM_MB" -gt 0 ]; then
info "VRAM < 8GB推荐模型: $MODEL"
else
info "未检测到 GPU使用轻量模型: $MODEL"
fi
fi
# 检测模型下载源
step "检测模型下载源..."
if curl -s --max-time 5 https://huggingface.co > /dev/null 2>&1; then
info "HuggingFace 可达,使用默认源"
else
warn "HuggingFace 不可达,自动切换到 ModelScope"
export OLLAMA_MODELS="https://modelscope.cn/models"
fi
info "正在拉取 $MODEL(首次下载可能需要几分钟)..."
if ollama pull "$MODEL"; then
info "模型 $MODEL 已就绪"
else
warn "模型拉取失败,请稍后手动执行: ollama pull $MODEL"
fi
fi
# ── Step 5.5: SearXNG 搜索服务 ──────────────────────────────
step "启动搜索服务SearXNG..."
if command -v docker >/dev/null 2>&1; then
# 已在运行则跳过
if docker ps --format '{{.Names}}' | grep -q "^kwcode-searxng$"; then
info "SearXNG 已在运行,跳过"
elif docker ps -a --format '{{.Names}}' | grep -q "^kwcode-searxng$"; then
docker start kwcode-searxng
info "SearXNG 已重新启动"
else
info "拉取 SearXNG 镜像约150MB..."
docker pull searxng/searxng
docker run -d \
--name kwcode-searxng \
--restart always \
-p 8080:8080 \
searxng/searxng
info "等待 SearXNG 启动..."
for i in $(seq 1 15); do
if curl -s http://localhost:8080 > /dev/null 2>&1; then
info "SearXNG 已就绪http://localhost:8080"
break
fi
sleep 1
done
fi
else
warn "未检测到 Docker搜索增强将使用 DuckDuckGo 降级方案"
info "安装 Docker 可获得更好的搜索体验https://docs.docker.com/get-docker/"
fi
# ── Step 6: kwcode init ──────────────────────────────────────
step "初始化 KwCode..."
if command -v kwcode >/dev/null 2>&1; then
kwcode init 2>/dev/null && info "KAIWU.md 已初始化" || info "跳过初始化(可稍后在项目目录执行 kwcode init"
else
info "kwcode 命令未在 PATH 中,跳过初始化"
info "尝试: $PYTHON_CMD -m kaiwu init"
fi
# ── Step 7: kwcode status ────────────────────────────────────
# ── Step 4: 验证安装 ─────────────────────────────────────────
step "验证安装..."
if command -v kwcode >/dev/null 2>&1; then
kwcode status || warn "状态检查失败,但安装可能已成功"
else
"$PYTHON_CMD" -m kaiwu status 2>/dev/null || warn "状态检查失败"
"$PYTHON_CMD" -m kwcode status 2>/dev/null || warn "状态检查失败"
fi
# ── 完成 ─────────────────────────────────────────────────────
@@ -247,5 +129,5 @@ printf " 2. kwcode init # 初始化项目记忆\n"
printf " 3. kwcode # 进入交互模式\n"
printf ' 4. kwcode "修复登录bug" # 直接执行任务\n'
printf "\n"
printf " ${GRAY}文档: https://github.com/kaiwu-agent/kaiwu${NC}\n"
printf " ${GRAY}文档: https://github.com/val1813/kwcode${NC}\n"
printf "\n"

5
kaiwu/__main__.py Normal file
View File

@@ -0,0 +1,5 @@
"""Allow running kwcode as: python -m kaiwu"""
from kaiwu.cli.main import app
if __name__ == "__main__":
app()

View File

@@ -1,16 +1,25 @@
"""
AST Engine: tree-sitter based call graph locator (spec §6).
AST Engine: tree-sitter based call graph locator (spec S6).
Provides function-level code location via call graph analysis.
BM25+graph retrieval (spec §LOC upgrade).
BM25+graph retrieval (spec LOC upgrade).
"""
from kaiwu.ast_engine.parser import TreeSitterParser
from kaiwu.ast_engine.call_graph import CallGraph
from kaiwu.ast_engine.locator import ASTLocator
from kaiwu.ast_engine.graph_builder import GraphBuilder
from kaiwu.ast_engine.graph_retriever import GraphRetriever
try:
from kaiwu.ast_engine.parser import TreeSitterParser
from kaiwu.ast_engine.call_graph import CallGraph
from kaiwu.ast_engine.locator import ASTLocator
from kaiwu.ast_engine.graph_builder import GraphBuilder
from kaiwu.ast_engine.graph_retriever import GraphRetriever
AST_AVAILABLE = True
except ImportError:
TreeSitterParser = None
CallGraph = None
ASTLocator = None
GraphBuilder = None
GraphRetriever = None
AST_AVAILABLE = False
__all__ = [
"TreeSitterParser", "CallGraph", "ASTLocator",
"GraphBuilder", "GraphRetriever",
"GraphBuilder", "GraphRetriever", "AST_AVAILABLE",
]

View File

@@ -4,36 +4,57 @@ build-backend = "setuptools.build_meta"
[project]
name = "kwcode"
version = "1.0.0"
version = "1.0.7"
description = "KwCode - Local-model coding agent with MoE expert pipeline"
requires-python = ">=3.10"
readme = "README.md"
license = "MIT"
keywords = ["coding-agent", "local-llm", "ollama", "code-generation"]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Code Generators",
]
dependencies = [
"llama-cpp-python>=0.2.90",
"typer>=0.12.0",
"rich>=13.0.0",
"pydantic>=2.0.0",
"httpx>=0.27.0",
"gitpython>=3.1.0",
"psutil>=5.9.0",
"beautifulsoup4>=4.12.0",
"lxml>=5.0.0",
"trafilatura>=1.12.0",
"pyyaml>=6.0",
"tree-sitter>=0.23.0",
"tree-sitter-python>=0.23.0",
"networkx>=3.0",
"rank-bm25>=0.2.2",
"pytest>=7.0.0",
"aiosqlite>=0.20.0",
"prompt-toolkit>=3.0.0",
]
[project.optional-dependencies]
rerank = ["sentence-transformers>=2.7.0"]
local = ["llama-cpp-python>=0.2.90"]
ast = ["tree-sitter>=0.23.0", "tree-sitter-python>=0.23.0"]
full = [
"kwcode[local]",
"kwcode[ast]",
]
dev = [
"pytest>=7.0.0",
"kwcode[full]",
]
[project.scripts]
kwcode = "kaiwu.cli.main:app"
[project.urls]
Homepage = "https://github.com/val1813/kwcode"
Repository = "https://github.com/val1813/kwcode"
Issues = "https://github.com/val1813/kwcode/issues"
[tool.setuptools.packages.find]
where = ["."]
include = ["kaiwu*"]