Files
nixos-config/home/common/programs/claude/synthetic.nix
T
Miguel Palhas d61700d173
CI / lint (push) Successful in 29s
CI / eval (push) Successful in 2m0s
fix: make synclaude reach synthetic.new
Two dokploy MCP tools declare ttl with the draft-04 "exclusiveMinimum":
true. Synthetic validates tool schemas against draft-2020 and rejects the
whole request, so every session died on a bodiless 500. Deny those two.

Haiku moves to GLM Flash: Qwen3.6-27B rejects Claude Code multi-block
system prompt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 14:22:12 +01:00

77 lines
2.9 KiB
Nix

{
pkgs,
inputs,
...
}:
# Claude Code pointed at synthetic.new. The three model aliases below are bound
# to three Synthetic models, so `/model opus|sonnet|haiku` swaps between them
# live; anything else there is reachable by full id (`/model hf:...`).
let
claude = inputs.claude-code.packages.${pkgs.stdenv.hostPlatform.system}.default;
# `hf:` ids pin one exact model. The `syn:large:*` aliases in Synthetic's own
# docs move under you whenever they rotate what "large" means.
kimi = "hf:moonshotai/Kimi-K3";
glm = "hf:zai-org/GLM-5.2";
# Not Qwen3.6-27B for the haiku slot: its chat template rejects Claude Code's
# multi-block system prompt with "System message must be at the beginning."
glmFlash = "hf:zai-org/GLM-4.7-Flash";
in
{
home.packages = [
(pkgs.writeShellScriptBin "synclaude" ''
set -euo pipefail
if [ -z "''${SYNTHETIC_API_KEY:-}" ] && [ -r "$HOME/.env.claude" ]; then
set +u
# Silenced: the file greets missing tooling on stderr, which would
# otherwise print before every session.
. "$HOME/.env.claude" >/dev/null 2>&1
set -u
fi
if [ -z "''${SYNTHETIC_API_KEY:-}" ]; then
echo "synclaude: SYNTHETIC_API_KEY is not set" >&2
exit 1
fi
export ANTHROPIC_BASE_URL="https://api.synthetic.new/anthropic"
export ANTHROPIC_AUTH_TOKEN="$SYNTHETIC_API_KEY"
export ANTHROPIC_DEFAULT_OPUS_MODEL="${kimi}"
export ANTHROPIC_DEFAULT_SONNET_MODEL="${glm}"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="${glmFlash}"
export CLAUDE_CODE_SUBAGENT_MODEL="${glm}"
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
export CLAUDE_CODE_ATTRIBUTION_HEADER=0
# ~/.env.claude exports both. ANTHROPIC_MODEL outranks the DEFAULT_* trio
# above, and ANTHROPIC_API_KEY would send Anthropic's credential to
# Synthetic.
unset ANTHROPIC_MODEL ANTHROPIC_API_KEY
# These two dokploy tools spell ttl's bound `"exclusiveMinimum": true`,
# which is draft-04. Synthetic validates against draft-2020, where the key
# must be a number, and rejects the whole request a bare 500 with no body
# when streaming. Every session fails, not just ones that call these.
broken_tools=(
mcp__dokploy__dnsProvider-createRecord
mcp__dokploy__dnsProvider-updateRecord
)
# ~/.claude/settings.json pins model "opus[1m]". The [1m] context-window
# suffix survives alias resolution and reaches Synthetic as part of the id,
# which 404s there so pick the alias explicitly unless the caller did.
for arg in "$@"; do
case "$arg" in
--model | --model=*)
exec ${claude}/bin/claude --disallowedTools "''${broken_tools[@]}" "$@"
;;
esac
done
exec ${claude}/bin/claude --model sonnet --disallowedTools "''${broken_tools[@]}" "$@"
'')
];
}