Ollama v0.14+ speaks the Anthropic Messages API natively, so the registry drives it with a fixed 'ollama' token and runtime-listed models: static_token skips secret storage everywhere keys resolve, models_dynamic defers model validation to the live /api/tags list, and the setup wizard installs the right GPU variant and starts the service before listing local models. Doctor stops treating the endpoint port as part of the hostname. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019hLK3wsDuKVAC37GgDqg6H
176 lines
5.9 KiB
Bash
176 lines
5.9 KiB
Bash
# shellcheck shell=bash
|
|
# Sourced helpers for omarchy-cn-ai-* commands
|
|
|
|
CN_AI_PROVIDERS="$OMARCHY_PATH/cn/registry/ai-providers.json"
|
|
CN_AI_HARNESSES="$OMARCHY_PATH/cn/registry/ai-harnesses.json"
|
|
CN_AI_COMPAT="$OMARCHY_PATH/cn/registry/ai-compatibility.json"
|
|
CN_AI_PROFILE_DIR="$HOME/.config/omarchycn/ai/profiles"
|
|
CN_AI_CURRENT="$HOME/.config/omarchycn/ai/current"
|
|
|
|
cn_ai_provider_ids() { jq -r '.providers | keys[]' "$CN_AI_PROVIDERS"; }
|
|
cn_ai_harness_ids() { jq -r '.harnesses | keys[]' "$CN_AI_HARNESSES"; }
|
|
|
|
cn_ai_endpoint() {
|
|
jq -re --arg p "$1" --arg proto "$2" '.providers[$p].endpoints[$proto]' "$CN_AI_PROVIDERS"
|
|
}
|
|
|
|
cn_ai_models() {
|
|
jq -r --arg p "$1" '.providers[$p].models[].id' "$CN_AI_PROVIDERS"
|
|
}
|
|
|
|
cn_ai_model_by_alias() {
|
|
jq -re --arg p "$1" --arg a "$2" \
|
|
'.providers[$p].models[] | select(.aliases // [] | index($a)) | .id' "$CN_AI_PROVIDERS"
|
|
}
|
|
|
|
cn_ai_fast_model() {
|
|
cn_ai_model_by_alias "$1" fast 2>/dev/null || cn_ai_model_by_alias "$1" default-coding
|
|
}
|
|
|
|
cn_ai_harness_field() {
|
|
jq -re --arg h "$1" --arg f "$2" '.harnesses[$h][$f]' "$CN_AI_HARNESSES"
|
|
}
|
|
|
|
# Providers with a fixed token (local servers) need no stored secret.
|
|
# `// empty` keeps stdout clean when absent (bare -re would print "null").
|
|
cn_ai_static_token() {
|
|
jq -re --arg p "$1" '.providers[$p].static_token // empty' "$CN_AI_PROVIDERS"
|
|
}
|
|
|
|
cn_ai_resolve_key() {
|
|
cn_ai_static_token "$1" 2>/dev/null || omarchy-cn-ai-secret get "$1"
|
|
}
|
|
|
|
# Dynamic-model providers list models at runtime, not in the registry
|
|
cn_ai_models_dynamic() {
|
|
jq -e --arg p "$1" '.providers[$p].models_dynamic == true' "$CN_AI_PROVIDERS" > /dev/null
|
|
}
|
|
|
|
cn_ai_combo_level() {
|
|
jq -r --arg h "$1" --arg p "$2" '.combos[$h][$p].level // "unsupported"' "$CN_AI_COMPAT"
|
|
}
|
|
|
|
# Combo-specific model allowlist; empty output means no restriction
|
|
cn_ai_combo_models() {
|
|
jq -r --arg h "$1" --arg p "$2" '.combos[$h][$p].models // [] | .[]' "$CN_AI_COMPAT"
|
|
}
|
|
|
|
cn_ai_model_protocol() {
|
|
jq -r --arg p "$1" --arg m "$2" \
|
|
'.providers[$p].models[] | select(.id == $m) | .protocol // ""' "$CN_AI_PROVIDERS"
|
|
}
|
|
|
|
# Profile files are flat key="value" lines
|
|
cn_ai_profile_field() {
|
|
local file="$CN_AI_PROFILE_DIR/$1.toml" key="$2"
|
|
grep -E "^$key = " "$file" | head -1 | cut -d\" -f2
|
|
}
|
|
|
|
cn_ai_current_profile() {
|
|
if [[ ! -f $CN_AI_CURRENT ]]; then
|
|
echo "No default AI profile set (run: omarchycn ai profile use <name>)" >&2
|
|
return 1
|
|
fi
|
|
cat "$CN_AI_CURRENT"
|
|
}
|
|
|
|
# Print export statements for the profile's harness x provider combo.
|
|
# Secrets are resolved at launch time only; nothing is written to disk.
|
|
cn_ai_render_env() {
|
|
local harness="$1" provider="$2" model="$3" key="$4"
|
|
local base fast
|
|
|
|
case "$harness" in
|
|
claude-code)
|
|
base=$(cn_ai_endpoint "$provider" anthropic)
|
|
# Dynamic-model providers have no fast alias: every tier maps to the model
|
|
fast=$(cn_ai_fast_model "$provider" 2>/dev/null || printf '%s' "$model")
|
|
printf 'export ANTHROPIC_BASE_URL=%q\n' "$base"
|
|
# Docs vary between AUTH_TOKEN (deepseek/zai) and API_KEY (kimi): set both
|
|
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$key"
|
|
printf 'export ANTHROPIC_API_KEY=%q\n' "$key"
|
|
printf 'export ANTHROPIC_MODEL=%q\n' "$model"
|
|
printf 'export ANTHROPIC_DEFAULT_SONNET_MODEL=%q\n' "$model"
|
|
printf 'export ANTHROPIC_DEFAULT_OPUS_MODEL=%q\n' "$model"
|
|
printf 'export ANTHROPIC_DEFAULT_HAIKU_MODEL=%q\n' "$fast"
|
|
;;
|
|
opencode)
|
|
if [[ $provider != "deepseek" ]]; then
|
|
echo "opencode adapter renders only the deepseek combo (native provider)" >&2
|
|
return 1
|
|
fi
|
|
printf 'export DEEPSEEK_API_KEY=%q\n' "$key"
|
|
;;
|
|
*)
|
|
echo "No env renderer for harness: $harness" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Write ~/.codex/config.toml + models.json per DeepSeek's official codex
|
|
# integration contract (mirrors cdn.deepseek.com codex-deepseek-setup script)
|
|
cn_ai_render_codex_config() {
|
|
local provider="$1" model="$2" key="$3"
|
|
local base cfg="$HOME/.codex/config.toml"
|
|
|
|
if [[ $provider != "deepseek" ]]; then
|
|
echo "codex adapter renders only the deepseek combo (official integration)" >&2
|
|
return 1
|
|
fi
|
|
base="https://api.deepseek.com/"
|
|
|
|
mkdir -p "${cfg%/*}"
|
|
touch "$cfg"
|
|
|
|
# Strip our old blocks first, then check for an unmanaged provider table
|
|
rm -f "$cfg.omarchycn-tmp" "$cfg.omarchycn-new"
|
|
(
|
|
umask 077
|
|
awk '
|
|
/^# OmarchyCN ai (model|provider) begin$/ { skip = 1; next }
|
|
/^# OmarchyCN ai (model|provider) end$/ { skip = 0; next }
|
|
skip { next }
|
|
/^[[:space:]]*\[/ { in_section = 1 }
|
|
!in_section && /^[[:space:]]*(model|model_provider|model_catalog_json|preferred_auth_method|forced_login_method|model_reasoning_effort)[[:space:]]*=/ { next }
|
|
{ print }
|
|
' "$cfg" > "$cfg.omarchycn-tmp"
|
|
)
|
|
|
|
if grep -qE "^[[:space:]]*\[[[:space:]]*model_providers[[:space:]]*\.[[:space:]]*\"?$provider\"?[[:space:]]*\]" "$cfg.omarchycn-tmp"; then
|
|
rm -f "$cfg.omarchycn-tmp"
|
|
echo "codex: $cfg 已有非托管的 [model_providers.$provider],请手动清理后重试" >&2
|
|
return 1
|
|
fi
|
|
|
|
if [[ -s $cfg ]]; then
|
|
cp "$cfg" "$cfg.omarchycn-bak-$(date +%Y%m%d-%H%M%S)"
|
|
fi
|
|
|
|
cp "$OMARCHY_PATH/cn/registry/codex-deepseek-models.json" "$HOME/.codex/models.json"
|
|
|
|
(
|
|
umask 077
|
|
{
|
|
echo "# OmarchyCN ai model begin"
|
|
echo "model = \"$model\""
|
|
echo "model_provider = \"$provider\""
|
|
echo "model_catalog_json = \"~/.codex/models.json\""
|
|
echo "preferred_auth_method = \"apikey\""
|
|
echo "forced_login_method = \"api\""
|
|
echo "model_reasoning_effort = \"high\""
|
|
echo "# OmarchyCN ai model end"
|
|
cat "$cfg.omarchycn-tmp"
|
|
echo "# OmarchyCN ai provider begin"
|
|
echo "[model_providers.$provider]"
|
|
echo "name = \"$provider\""
|
|
echo "base_url = \"$base\""
|
|
echo "wire_api = \"responses\""
|
|
echo "experimental_bearer_token = \"$key\""
|
|
echo "# OmarchyCN ai provider end"
|
|
} > "$cfg.omarchycn-new"
|
|
)
|
|
mv "$cfg.omarchycn-new" "$cfg"
|
|
rm -f "$cfg.omarchycn-tmp"
|
|
}
|