mirror of
https://codeberg.org/muon/home.git
synced 2026-07-03 23:49:35 +00:00
Add hermes
This commit is contained in:
parent
28bb03187d
commit
609b3384c0
9 changed files with 526 additions and 6 deletions
227
flake.lock
generated
227
flake.lock
generated
|
|
@ -132,6 +132,27 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts": {
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772408722,
|
||||||
|
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": [
|
||||||
"nvf",
|
"nvf",
|
||||||
|
|
@ -152,7 +173,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts_2": {
|
"flake-parts_3": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": [
|
||||||
"stylix",
|
"stylix",
|
||||||
|
|
@ -224,6 +245,29 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hermes-agent": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"npm-lockfile-fix": "npm-lockfile-fix",
|
||||||
|
"pyproject-build-systems": "pyproject-build-systems",
|
||||||
|
"pyproject-nix": "pyproject-nix_2",
|
||||||
|
"uv2nix": "uv2nix_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780309788,
|
||||||
|
"narHash": "sha256-h0pN9UHOL0g0S0tc2Eqhgn8DA6Y5b5dvoOZoO6ApE2o=",
|
||||||
|
"owner": "NousResearch",
|
||||||
|
"repo": "hermes-agent",
|
||||||
|
"rev": "ef3a650f05d2e9ce14855af1d0184f3ee93455da",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NousResearch",
|
||||||
|
"repo": "hermes-agent",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"home-manager": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
|
@ -426,6 +470,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775036866,
|
||||||
|
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778869304,
|
"lastModified": 1778869304,
|
||||||
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
||||||
|
|
@ -441,6 +501,27 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"npm-lockfile-fix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775903712,
|
||||||
|
"narHash": "sha256-2GV79U6iVH4gKAPWYrxUReB0S41ty/Y3dBLquU8AlaA=",
|
||||||
|
"owner": "jeslie0",
|
||||||
|
"repo": "npm-lockfile-fix",
|
||||||
|
"rev": "c6093acb0c0548e0f9b8b3d82918823721930fe8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "jeslie0",
|
||||||
|
"repo": "npm-lockfile-fix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nur": {
|
"nur": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": [
|
"flake-parts": [
|
||||||
|
|
@ -469,7 +550,7 @@
|
||||||
"nvf": {
|
"nvf": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_3",
|
"flake-compat": "flake-compat_3",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts_2",
|
||||||
"mnw": "mnw",
|
"mnw": "mnw",
|
||||||
"ndg": "ndg",
|
"ndg": "ndg",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
|
@ -491,14 +572,103 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pyproject-build-systems": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"pyproject-nix": "pyproject-nix",
|
||||||
|
"uv2nix": "uv2nix"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772555609,
|
||||||
|
"narHash": "sha256-3BA3HnUvJSbHJAlJj6XSy0Jmu7RyP2gyB/0fL7XuEDo=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "build-system-pkgs",
|
||||||
|
"rev": "c37f66a953535c394244888598947679af231863",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "build-system-pkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pyproject-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"pyproject-build-systems",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769936401,
|
||||||
|
"narHash": "sha256-kwCOegKLZJM9v/e/7cqwg1p/YjjTAukKPqmxKnAZRgA=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"rev": "b0d513eeeebed6d45b4f2e874f9afba2021f7812",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pyproject-nix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772865871,
|
||||||
|
"narHash": "sha256-/ZTSg97aouL0SlPHaokA4r3iuH9QzHVuWPACD2CUCFY=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"rev": "e537db02e72d553cea470976b9733581bcf5b3ed",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pyproject-nix_3": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"uv2nix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771518446,
|
||||||
|
"narHash": "sha256-nFJSfD89vWTu92KyuJWDoTQJuoDuddkJV3TlOl1cOic=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"rev": "eb204c6b3335698dec6c7fc1da0ebc3c6df05937",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"hermes-agent": "hermes-agent",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"impermanence": "impermanence",
|
"impermanence": "impermanence",
|
||||||
"nix-alien": "nix-alien",
|
"nix-alien": "nix-alien",
|
||||||
"nix-flatpak": "nix-flatpak",
|
"nix-flatpak": "nix-flatpak",
|
||||||
"nix-minecraft": "nix-minecraft",
|
"nix-minecraft": "nix-minecraft",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nvf": "nvf",
|
"nvf": "nvf",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"stylix": "stylix",
|
"stylix": "stylix",
|
||||||
|
|
@ -554,7 +724,7 @@
|
||||||
"base16-helix": "base16-helix",
|
"base16-helix": "base16-helix",
|
||||||
"base16-vim": "base16-vim",
|
"base16-vim": "base16-vim",
|
||||||
"firefox-gnome-theme": "firefox-gnome-theme",
|
"firefox-gnome-theme": "firefox-gnome-theme",
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts_3",
|
||||||
"gnome-shell": "gnome-shell",
|
"gnome-shell": "gnome-shell",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
|
|
@ -689,6 +859,55 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"uv2nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"pyproject-build-systems",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"pyproject-nix": [
|
||||||
|
"hermes-agent",
|
||||||
|
"pyproject-build-systems",
|
||||||
|
"pyproject-nix"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770770348,
|
||||||
|
"narHash": "sha256-A2GzkmzdYvdgmMEu5yxW+xhossP+txrYb7RuzRaqhlg=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "uv2nix",
|
||||||
|
"rev": "5d1b2cb4fe3158043fbafbbe2e46238abbc954b0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "uv2nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uv2nix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"pyproject-nix": "pyproject-nix_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1773039484,
|
||||||
|
"narHash": "sha256-+boo33KYkJDw9KItpeEXXv8+65f7hHv/earxpcyzQ0I=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "uv2nix",
|
||||||
|
"rev": "b68be7cfeacbed9a3fa38a2b5adc0cfb81d9bb1f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "uv2nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"valheim-server": {
|
"valheim-server": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@
|
||||||
valheim-server.inputs.nixpkgs.follows = "nixpkgs";
|
valheim-server.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
nix-flatpak.url = "github:gmodena/nix-flatpak?ref=latest";
|
nix-flatpak.url = "github:gmodena/nix-flatpak?ref=latest";
|
||||||
|
|
||||||
|
hermes-agent.url = "github:NousResearch/hermes-agent";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs @ {
|
outputs = inputs @ {
|
||||||
|
|
@ -103,6 +105,7 @@
|
||||||
inputs.home-manager.nixosModules.default
|
inputs.home-manager.nixosModules.default
|
||||||
inputs.stylix.nixosModules.stylix
|
inputs.stylix.nixosModules.stylix
|
||||||
inputs.impermanence.nixosModules.impermanence
|
inputs.impermanence.nixosModules.impermanence
|
||||||
|
inputs.hermes-agent.nixosModules.default
|
||||||
];
|
];
|
||||||
|
|
||||||
home-manager.sharedModules = [
|
home-manager.sharedModules = [
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,11 @@ in {
|
||||||
# port defaults to 8317
|
# port defaults to 8317
|
||||||
# Run `auth2api --login` once after deploying to authorise your Claude account.
|
# Run `auth2api --login` once after deploying to authorise your Claude account.
|
||||||
|
|
||||||
|
mods.server.hermes.enable = true;
|
||||||
|
# model defaults to claude-sonnet-4-6 (auth2api alias) via local proxy
|
||||||
|
# container defaults to true (Docker backend) — Docker already enabled above
|
||||||
|
# Run `hermes version` after deploying to confirm the CLI is on PATH.
|
||||||
|
|
||||||
mods.server.sync.enable = true;
|
mods.server.sync.enable = true;
|
||||||
mods.tailscale.enable = true;
|
mods.tailscale.enable = true;
|
||||||
mods.openvpn.enable = false;
|
mods.openvpn.enable = false;
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,11 @@ let
|
||||||
# off | errors | verbose
|
# off | errors | verbose
|
||||||
debug: "${cfg.debug}"
|
debug: "${cfg.debug}"
|
||||||
|
|
||||||
|
thinking:
|
||||||
|
# Default reasoning effort for requests without reasoning_effort set.
|
||||||
|
# none | minimal | low | medium | high | xhigh
|
||||||
|
default-effort: "${cfg.thinking.defaultEffort}"
|
||||||
|
|
||||||
cloaking:
|
cloaking:
|
||||||
cli-version: "2.1.88"
|
cli-version: "2.1.88"
|
||||||
entrypoint: "cli"
|
entrypoint: "cli"
|
||||||
|
|
@ -110,6 +115,23 @@ with lib; {
|
||||||
description = "Log level: off | errors | verbose.";
|
description = "Log level: off | errors | verbose.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
thinking = {
|
||||||
|
defaultEffort = mkOption {
|
||||||
|
type = types.enum [ "none" "minimal" "low" "medium" "high" "xhigh" ];
|
||||||
|
default = "medium";
|
||||||
|
description = ''
|
||||||
|
Default Claude extended-thinking budget injected into every request
|
||||||
|
that doesn't already carry a reasoning_effort field.
|
||||||
|
none = thinking disabled
|
||||||
|
minimal = 512 tokens
|
||||||
|
low = 1 024 tokens
|
||||||
|
medium = 8 192 tokens (default)
|
||||||
|
high = 24 576 tokens
|
||||||
|
xhigh = 32 768 tokens
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
openFirewall = mkOption {
|
openFirewall = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,6 @@
|
||||||
./atuin.nix
|
./atuin.nix
|
||||||
./murmur.nix
|
./murmur.nix
|
||||||
./auth2api.nix
|
./auth2api.nix
|
||||||
|
./hermes.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
136
modules/nixos/server/hermes.nix
Normal file
136
modules/nixos/server/hermes.nix
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.mods.server.hermes;
|
||||||
|
auth2apiCfg = config.mods.server.auth2api;
|
||||||
|
|
||||||
|
# auth2api exposes an OpenAI-compatible endpoint; point Hermes at it.
|
||||||
|
auth2apiUrl = "http://${auth2apiCfg.host}:${toString auth2apiCfg.port}/v1";
|
||||||
|
in
|
||||||
|
with lib; {
|
||||||
|
options.mods.server.hermes = {
|
||||||
|
enable = mkEnableOption "Hermes AI agent (NousResearch)";
|
||||||
|
|
||||||
|
model = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "claude-sonnet-4-6";
|
||||||
|
description = ''
|
||||||
|
Model identifier as exposed by auth2api. Available aliases:
|
||||||
|
sonnet, haiku, opus, claude-sonnet-4-6, claude-haiku-4-5, claude-opus-4-6, etc.
|
||||||
|
Run: curl http://127.0.0.1:8317/v1/models
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
container = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Run Hermes inside a persistent Ubuntu container.";
|
||||||
|
};
|
||||||
|
|
||||||
|
hostUsers = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ config.mods.user.name ];
|
||||||
|
description = ''
|
||||||
|
Users that get a ~/.hermes symlink to the service stateDir and are
|
||||||
|
added to the hermes group for shared file access.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraVolumes = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
|
description = "Extra volume mounts (host:container:mode) for the container.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
addToSystemPackages = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Put the hermes CLI on the system PATH and set HERMES_HOME so the
|
||||||
|
interactive CLI shares state with the gateway service.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraPackages = mkOption {
|
||||||
|
type = types.listOf types.package;
|
||||||
|
default = [ ];
|
||||||
|
description = "Extra Nix packages available to the agent at runtime.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/var/lib/hermes";
|
||||||
|
description = "State directory (HERMES_HOME parent).";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Hermes needs auth2api running to have a backend to call.
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = auth2apiCfg.enable;
|
||||||
|
message = "mods.server.hermes requires mods.server.auth2api.enable = true";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
services.hermes-agent = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
inherit (cfg) stateDir addToSystemPackages extraPackages;
|
||||||
|
|
||||||
|
# auth2api runs unauthenticated on localhost (api-keys: []), but Hermes's
|
||||||
|
# provider resolver requires a non-empty key env var for any provider.
|
||||||
|
# AUTH2API_KEY is a dummy — auth2api ignores Bearer tokens when api-keys: [].
|
||||||
|
environment.AUTH2API_KEY = "auth2api";
|
||||||
|
|
||||||
|
# ── Model ──────────────────────────────────────────────────────────────
|
||||||
|
# Declare auth2api as a named custom provider in the providers: section.
|
||||||
|
# Hermes resolves it via resolve_user_provider, which accepts arbitrary
|
||||||
|
# base_url + key_env without needing a built-in provider slug.
|
||||||
|
settings = {
|
||||||
|
providers.auth2api = {
|
||||||
|
name = "auth2api (Claude via OAuth)";
|
||||||
|
api = auth2apiUrl;
|
||||||
|
key_env = "AUTH2API_KEY";
|
||||||
|
transport = "openai_chat";
|
||||||
|
};
|
||||||
|
|
||||||
|
model = {
|
||||||
|
default = cfg.model;
|
||||||
|
provider = "auth2api";
|
||||||
|
};
|
||||||
|
|
||||||
|
toolsets = [ "all" ];
|
||||||
|
max_turns = 100;
|
||||||
|
|
||||||
|
terminal = {
|
||||||
|
backend = "local";
|
||||||
|
timeout = 180;
|
||||||
|
};
|
||||||
|
|
||||||
|
memory = {
|
||||||
|
memory_enabled = true;
|
||||||
|
user_profile_enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
display = {
|
||||||
|
compact = false;
|
||||||
|
show_reasoning = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# ── Container ──────────────────────────────────────────────────────────
|
||||||
|
container = mkIf cfg.container.enable {
|
||||||
|
enable = true;
|
||||||
|
backend = "docker";
|
||||||
|
inherit (cfg.container) hostUsers extraVolumes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
buildNpmPackage {
|
buildNpmPackage {
|
||||||
pname = "auth2api";
|
pname = "auth2api";
|
||||||
version = sources.auth2api.version;
|
version = "${sources.auth2api.version}-thinking3";
|
||||||
src = sources.auth2api.src;
|
src = sources.auth2api.src;
|
||||||
|
|
||||||
nodejs = nodejs_20;
|
nodejs = nodejs_20;
|
||||||
|
|
@ -17,7 +17,10 @@ buildNpmPackage {
|
||||||
|
|
||||||
# Patch to allow running with an empty api-keys list (unauthenticated).
|
# Patch to allow running with an empty api-keys list (unauthenticated).
|
||||||
# Safe because the service binds to 127.0.0.1 by default.
|
# Safe because the service binds to 127.0.0.1 by default.
|
||||||
patches = [ ./no-auth.patch ];
|
patches = [
|
||||||
|
./no-auth.patch
|
||||||
|
./thinking.patch
|
||||||
|
];
|
||||||
|
|
||||||
# auth2api's build script is `tsc` (TypeScript compile → dist/)
|
# auth2api's build script is `tsc` (TypeScript compile → dist/)
|
||||||
# devDeps (typescript, tsx) are needed for the build; buildNpmPackage
|
# devDeps (typescript, tsx) are needed for the build; buildNpmPackage
|
||||||
|
|
|
||||||
130
pkgs/auth2api/thinking.patch
Normal file
130
pkgs/auth2api/thinking.patch
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
--- a/src/config.ts
|
||||||
|
+++ b/src/config.ts
|
||||||
|
@@ -44,6 +44,12 @@
|
||||||
|
"count-tokens-ms": number;
|
||||||
|
}
|
||||||
|
|
||||||
|
+export interface ThinkingConfig {
|
||||||
|
+ /** Default reasoning effort when the client sends no reasoning_effort.
|
||||||
|
+ * Accepts: none | minimal | low | medium | high | xhigh (default: medium) */
|
||||||
|
+ "default-effort": string;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
export interface StatsConfig {
|
||||||
|
/** Default true. Set false to disable per-request stats recording entirely. */
|
||||||
|
enabled: boolean;
|
||||||
|
@@ -58,6 +64,7 @@
|
||||||
|
"api-keys": Set<string>;
|
||||||
|
"body-limit": string;
|
||||||
|
cloaking: CloakingConfig;
|
||||||
|
+ thinking: ThinkingConfig;
|
||||||
|
timeouts: TimeoutConfig;
|
||||||
|
stats: StatsConfig;
|
||||||
|
debug: DebugMode;
|
||||||
|
@@ -78,6 +85,9 @@
|
||||||
|
"cli-version": "2.1.88",
|
||||||
|
entrypoint: "cli",
|
||||||
|
},
|
||||||
|
+ thinking: {
|
||||||
|
+ "default-effort": "medium",
|
||||||
|
+ },
|
||||||
|
timeouts: {
|
||||||
|
"messages-ms": 120000,
|
||||||
|
"stream-messages-ms": 600000,
|
||||||
|
--- a/src/handlers/openai.ts
|
||||||
|
+++ b/src/handlers/openai.ts
|
||||||
|
@@ -540,7 +540,13 @@
|
||||||
|
const structured =
|
||||||
|
body.response_format?.type === "json_object" ||
|
||||||
|
body.response_format?.type === "json_schema";
|
||||||
|
- const translatedBody = openaiToAnthropic(body);
|
||||||
|
+ // Inject default thinking effort when the client hasn't specified one.
|
||||||
|
+ const effort = config.thinking?.["default-effort"] ?? "medium";
|
||||||
|
+ const bodyWithThinking =
|
||||||
|
+ body.reasoning_effort || effort === "none"
|
||||||
|
+ ? body
|
||||||
|
+ : { ...body, reasoning_effort: effort };
|
||||||
|
+ const translatedBody = openaiToAnthropic(bodyWithThinking);
|
||||||
|
|
||||||
|
if (isDebugLevel(config.debug, "verbose")) {
|
||||||
|
console.log(
|
||||||
|
--- a/src/upstream/anthropic-api.ts
|
||||||
|
+++ b/src/upstream/anthropic-api.ts
|
||||||
|
@@ -18,17 +18,17 @@
|
||||||
|
|
||||||
|
if (isHaiku) {
|
||||||
|
if (structured) {
|
||||||
|
- return "oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05,structured-outputs-2025-12-15";
|
||||||
|
+ return "oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,structured-outputs-2025-12-15";
|
||||||
|
} else {
|
||||||
|
- return "oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05,claude-code-20250219";
|
||||||
|
+ return "oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,claude-code-20250219";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (structured) {
|
||||||
|
- return "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24,structured-outputs-2025-12-15";
|
||||||
|
+ return "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24,structured-outputs-2025-12-15";
|
||||||
|
}
|
||||||
|
|
||||||
|
- return "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24";
|
||||||
|
+ return "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
--- a/src/upstream/translator.ts
|
||||||
|
+++ b/src/upstream/translator.ts
|
||||||
|
@@ -9,13 +9,14 @@
|
||||||
|
inputTokens: number,
|
||||||
|
outputTokens: number,
|
||||||
|
cachedTokens: number,
|
||||||
|
+ reasoningTokens: number = 0,
|
||||||
|
): any {
|
||||||
|
return {
|
||||||
|
prompt_tokens: inputTokens,
|
||||||
|
completion_tokens: outputTokens,
|
||||||
|
total_tokens: inputTokens + outputTokens,
|
||||||
|
prompt_tokens_details: { cached_tokens: cachedTokens },
|
||||||
|
- completion_tokens_details: { reasoning_tokens: 0 },
|
||||||
|
+ completion_tokens_details: { reasoning_tokens: reasoningTokens },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -281,6 +282,7 @@
|
||||||
|
|
||||||
|
export function anthropicToOpenai(anthropicResp: any, model: string): any {
|
||||||
|
let textContent = "";
|
||||||
|
+ let reasoningContent = "";
|
||||||
|
const toolCalls: any[] = [];
|
||||||
|
|
||||||
|
if (Array.isArray(anthropicResp.content)) {
|
||||||
|
@@ -288,7 +290,7 @@
|
||||||
|
if (block.type === "text") {
|
||||||
|
textContent += block.text;
|
||||||
|
} else if (block.type === "thinking" && block.thinking) {
|
||||||
|
- // thinking blocks not exposed in chat completions response
|
||||||
|
+ reasoningContent += block.thinking;
|
||||||
|
} else if (block.type === "tool_use") {
|
||||||
|
toolCalls.push({
|
||||||
|
id: block.id,
|
||||||
|
@@ -303,10 +305,12 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
const message: any = { role: "assistant", content: textContent || null };
|
||||||
|
+ if (reasoningContent) message.reasoning_content = reasoningContent;
|
||||||
|
if (toolCalls.length) message.tool_calls = toolCalls;
|
||||||
|
|
||||||
|
const inputTokens = anthropicResp.usage?.input_tokens || 0;
|
||||||
|
const outputTokens = anthropicResp.usage?.output_tokens || 0;
|
||||||
|
+ const reasoningTokens = anthropicResp.usage?.output_tokens_details?.thinking_tokens || 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: `chatcmpl-${uuidv4()}`,
|
||||||
|
@@ -325,6 +329,7 @@
|
||||||
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
|
anthropicResp.usage?.cache_read_input_tokens || 0,
|
||||||
|
+ reasoningTokens,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ in {
|
||||||
inputs.home-manager.nixosModules.default
|
inputs.home-manager.nixosModules.default
|
||||||
inputs.stylix.nixosModules.stylix
|
inputs.stylix.nixosModules.stylix
|
||||||
inputs.impermanence.nixosModules.impermanence
|
inputs.impermanence.nixosModules.impermanence
|
||||||
|
inputs.hermes-agent.nixosModules.default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue