haru-cli 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {haru_cli-0.1.0 → haru_cli-0.1.2}/CHANGELOG.md +32 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/PKG-INFO +55 -8
- haru_cli-0.1.2/README.md +139 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/agents.yaml +7 -0
- haru_cli-0.1.2/config/models.yaml +31 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/docs/configuration.md +12 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/pyproject.toml +1 -1
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/agents/factory.py +28 -7
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/cli.py +4 -0
- haru_cli-0.1.2/src/haru/commands/agents.py +42 -0
- haru_cli-0.1.2/src/haru/commands/chat.py +316 -0
- haru_cli-0.1.2/src/haru/commands/config.py +163 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/login.py +13 -9
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/run.py +35 -8
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/session.py +9 -3
- haru_cli-0.1.2/src/haru/config/__init__.py +19 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/config/loader.py +33 -1
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/config/schema.py +20 -1
- haru_cli-0.1.2/src/haru/config/templates/__init__.py +1 -0
- haru_cli-0.1.2/src/haru/config/templates/agents.yaml +37 -0
- haru_cli-0.1.2/src/haru/config/templates/haru.yaml +24 -0
- haru_cli-0.1.2/src/haru/config/templates/logging.yaml +12 -0
- haru_cli-0.1.2/src/haru/config/templates/mcp.yaml +15 -0
- haru_cli-0.1.2/src/haru/config/templates/models.yaml +31 -0
- haru_cli-0.1.2/src/haru/config/templates/prompts/researcher.md +14 -0
- haru_cli-0.1.2/src/haru/config/templates/prompts/supervisor.md +15 -0
- haru_cli-0.1.2/src/haru/config/templates/prompts/writer.md +12 -0
- haru_cli-0.1.2/src/haru/models/bedrock.py +146 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_agent_factory.py +36 -1
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_bedrock.py +74 -3
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_cli.py +173 -2
- haru_cli-0.1.2/tests/unit/test_config_command.py +134 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_config_loader.py +51 -1
- {haru_cli-0.1.0 → haru_cli-0.1.2}/uv.lock +1 -1
- haru_cli-0.1.0/README.md +0 -92
- haru_cli-0.1.0/config/models.yaml +0 -20
- haru_cli-0.1.0/src/haru/commands/chat.py +0 -102
- haru_cli-0.1.0/src/haru/config/__init__.py +0 -6
- haru_cli-0.1.0/src/haru/models/bedrock.py +0 -77
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.github/workflows/ci.yml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.github/workflows/release.yml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.gitignore +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/coding-standards.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/git-workflow.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/product.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/security.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/structure.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/tech.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/testing.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.pre-commit-config.yaml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/.python-version +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/CLAUDE.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/LICENSE +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/guardrails.yaml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/haru.yaml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/logging.yaml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/mcp.yaml +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/prompts/researcher.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/prompts/supervisor.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/config/prompts/writer.md +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/renovate.json +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/__main__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/agents/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/agents/orchestration.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/cache.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/pkce.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/session.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/sso.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/streaming.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/errors.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/models/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/observability/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/observability/guardrails.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/observability/telemetry.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/sessions/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/sessions/manager.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/steering/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/steering/prompts.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/tools/__init__.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/tools/mcp.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/tools/registry.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/conftest.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/integration/test_packaging.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_cache.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_guardrails.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_main.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_mcp.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_orchestration.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_pkce.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_prompts.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_session.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_sessions.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_sso.py +0 -0
- {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_telemetry.py +0 -0
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Sampling controls (`temperature`, `top_p`, `top_k`, `seed`) across three
|
|
13
|
+
surfaces: model entries, per-agent `sampling:` blocks, and CLI/REPL
|
|
14
|
+
overrides (`--temperature/--top-p/--top-k/--seed`, `/sampling`).
|
|
15
|
+
Precedence CLI/REPL → agent → model, merged per-field; unset fields are
|
|
16
|
+
omitted from requests. `top_k`/`seed` travel via Converse
|
|
17
|
+
`additionalModelRequestFields`.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `temperature` is no longer required (or sent by default) on model entries —
|
|
22
|
+
Claude 5-series and Opus 4.7+ reject non-default sampling values with an
|
|
23
|
+
HTTP 400, which the previous always-sent temperature would have triggered.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `haru config init`: interactive starter configuration under
|
|
28
|
+
`~/.config/haru` (SSO settings prompted; guardrails explicitly disabled
|
|
29
|
+
when no id is provided). `haru config show` prints a secret-free summary.
|
|
30
|
+
- Config resolution order: `--config`, `$HARU_CONFIG`, `./config/haru.yaml`,
|
|
31
|
+
`~/.config/haru/haru.yaml` — installed CLIs work outside a project clone.
|
|
32
|
+
- `haru agents`: list configured agents with model, prompt, tools, and MCP
|
|
33
|
+
servers.
|
|
34
|
+
- Chat REPL slash commands: `/help`, `/model` (list/switch models), `/agent`
|
|
35
|
+
(list/switch agents).
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- `haru login` reports configuration errors as clean messages instead of a
|
|
40
|
+
traceback, pointing at `haru config init`.
|
|
41
|
+
|
|
10
42
|
## [0.1.0] - 2026-07-26
|
|
11
43
|
|
|
12
44
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haru-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Secure, scriptable CLI for Amazon Bedrock Claude models via the AWS Strands Agents SDK.
|
|
5
5
|
Author: haru-cli maintainers
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -56,22 +56,38 @@ uv sync
|
|
|
56
56
|
|
|
57
57
|
| Command | Purpose |
|
|
58
58
|
| -------------------------------- | ------------------------------------------------------------- |
|
|
59
|
+
| `haru config init` | Create a starter configuration (interactive) |
|
|
60
|
+
| `haru config show` | Show the resolved configuration (no secrets) |
|
|
59
61
|
| `haru login` | Browser sign-in to IAM Identity Center (OAuth 2.0 + PKCE) |
|
|
60
62
|
| `haru chat` | Interactive streaming chat REPL |
|
|
61
63
|
| `haru chat --session-id <id>` | Persist and restore the conversation under a session id |
|
|
62
64
|
| `haru chat --agent <name>` | Chat with a specific configured agent |
|
|
63
65
|
| `haru run "<prompt>"` | One-shot prompt; prints the answer and exits |
|
|
66
|
+
| `haru agents` | List configured agents (model, prompt, tools, MCP) |
|
|
64
67
|
| `haru session list` | List stored session ids |
|
|
65
68
|
| `haru --version` | Print the installed version |
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
Configuration is resolved from `--config`, then `$HARU_CONFIG`, then
|
|
71
|
+
`./config/haru.yaml`, then `~/.config/haru/haru.yaml`.
|
|
72
|
+
|
|
73
|
+
Inside `haru chat`, slash commands switch targets mid-session:
|
|
74
|
+
|
|
75
|
+
| REPL command | Purpose |
|
|
76
|
+
| ---------------- | ---------------------------------------------------------- |
|
|
77
|
+
| `/help` | Show REPL commands |
|
|
78
|
+
| `/model` | List configured models (default and active marked) |
|
|
79
|
+
| `/model <name>` | Switch the default agent to that model (resets the chat) |
|
|
80
|
+
| `/agent` | List configured agents |
|
|
81
|
+
| `/agent <name>` | Switch to that agent (resets the chat) |
|
|
68
82
|
|
|
69
83
|
## Quickstart
|
|
70
84
|
|
|
71
85
|
```bash
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
haru config init # writes ~/.config/haru (asks for your SSO start URL)
|
|
87
|
+
export HARU_AWS_ACCOUNT_ID=<your AWS account id>
|
|
88
|
+
haru login
|
|
89
|
+
haru run "Summarise the Converse API in two sentences."
|
|
90
|
+
haru chat --agent supervisor --session-id demo
|
|
75
91
|
```
|
|
76
92
|
|
|
77
93
|
Authentication uses the same PKCE authorization-code flow as `aws sso login`,
|
|
@@ -80,12 +96,43 @@ caching tokens in the botocore-compatible schema under `~/.aws/sso/cache`
|
|
|
80
96
|
tokens are refreshed automatically; when a fresh login is needed, commands say
|
|
81
97
|
so plainly.
|
|
82
98
|
|
|
99
|
+
## Sampling controls
|
|
100
|
+
|
|
101
|
+
haru-cli exposes `temperature`, `top_p`, `top_k`, and `seed` on **three
|
|
102
|
+
configuration surfaces** — a capability Kiro CLI does not offer on any of its
|
|
103
|
+
configuration surfaces (its v2/v3 agent formats and `cli.json` define no
|
|
104
|
+
sampling fields):
|
|
105
|
+
|
|
106
|
+
| Surface | Where | Example |
|
|
107
|
+
| ------------------------ | ---------------------------------------------- | ----------------------------------------- |
|
|
108
|
+
| Model catalogue | `models.yaml`, per model entry | `temperature: 0.2`, `top_k: 50` |
|
|
109
|
+
| Agent profile | `agents.yaml`, per agent `sampling:` block | `sampling: {temperature: 0.1}` |
|
|
110
|
+
| CLI / REPL | `--temperature/--top-p/--top-k/--seed` flags on `run` and `chat`; `/sampling` in the REPL | `haru run "..." --top-k 20` |
|
|
111
|
+
|
|
112
|
+
Precedence: **CLI/REPL override → agent `sampling:` block → model entry**,
|
|
113
|
+
merged per-field. Unset fields are omitted from requests entirely, keeping
|
|
114
|
+
the provider's defaults. In the chat REPL, `/sampling` shows the model
|
|
115
|
+
default, session override, and effective value for each field;
|
|
116
|
+
`/sampling temperature=0.2 top_k=50` applies overrides and `/sampling reset`
|
|
117
|
+
clears them.
|
|
118
|
+
|
|
119
|
+
Model compatibility (AWS-side behaviour, not a haru limitation):
|
|
120
|
+
|
|
121
|
+
- **Claude 5-series and Opus 4.7+** (Sonnet 5, Opus 5, Opus 4.8) reject
|
|
122
|
+
non-default `temperature`/`top_p`/`top_k` with an HTTP 400 — leave them
|
|
123
|
+
unset on those entries (the shipped config does).
|
|
124
|
+
- **Claude Haiku 4.5, Sonnet 4.5, and older Claude models**, plus
|
|
125
|
+
non-Anthropic Bedrock models, accept them.
|
|
126
|
+
- `seed` is forwarded via Converse `additionalModelRequestFields` for Bedrock
|
|
127
|
+
models that support it; **no Claude model accepts a seed today**, so do not
|
|
128
|
+
expect determinism from Claude — the field exists for models that honour it.
|
|
129
|
+
|
|
83
130
|
## Configuration
|
|
84
131
|
|
|
85
132
|
Declarative YAML under `config/` — models, agents, orchestration
|
|
86
|
-
(supervisor/swarm/graph), MCP servers, guardrails, sessions,
|
|
87
|
-
OpenTelemetry. No secrets in YAML: values reference environment
|
|
88
|
-
`${env:VAR}`. See [docs/configuration.md](docs/configuration.md).
|
|
133
|
+
(supervisor/swarm/graph), MCP servers, guardrails, sessions, sampling,
|
|
134
|
+
logging, and OpenTelemetry. No secrets in YAML: values reference environment
|
|
135
|
+
variables with `${env:VAR}`. See [docs/configuration.md](docs/configuration.md).
|
|
89
136
|
|
|
90
137
|
## Development
|
|
91
138
|
|
haru_cli-0.1.2/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# haru-cli
|
|
2
|
+
|
|
3
|
+
A secure, scriptable command line interface for interacting with Amazon Bedrock Claude models
|
|
4
|
+
through governed, observable, multi-agent workflows. haru-cli is a thin, functional orchestration
|
|
5
|
+
layer over the [AWS Strands Agents SDK](https://strandsagents.com), talking to Bedrock through the
|
|
6
|
+
Converse API with streaming, tool use, MCP client support, multi-agent orchestration, session
|
|
7
|
+
persistence, OpenTelemetry observability, and Bedrock Guardrails.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Python 3.13+ (tested on 3.13 and 3.14)
|
|
12
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
13
|
+
- AWS access via IAM Identity Center (SSO)
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pipx install haru-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or from a clone:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv sync
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
| Command | Purpose |
|
|
30
|
+
| -------------------------------- | ------------------------------------------------------------- |
|
|
31
|
+
| `haru config init` | Create a starter configuration (interactive) |
|
|
32
|
+
| `haru config show` | Show the resolved configuration (no secrets) |
|
|
33
|
+
| `haru login` | Browser sign-in to IAM Identity Center (OAuth 2.0 + PKCE) |
|
|
34
|
+
| `haru chat` | Interactive streaming chat REPL |
|
|
35
|
+
| `haru chat --session-id <id>` | Persist and restore the conversation under a session id |
|
|
36
|
+
| `haru chat --agent <name>` | Chat with a specific configured agent |
|
|
37
|
+
| `haru run "<prompt>"` | One-shot prompt; prints the answer and exits |
|
|
38
|
+
| `haru agents` | List configured agents (model, prompt, tools, MCP) |
|
|
39
|
+
| `haru session list` | List stored session ids |
|
|
40
|
+
| `haru --version` | Print the installed version |
|
|
41
|
+
|
|
42
|
+
Configuration is resolved from `--config`, then `$HARU_CONFIG`, then
|
|
43
|
+
`./config/haru.yaml`, then `~/.config/haru/haru.yaml`.
|
|
44
|
+
|
|
45
|
+
Inside `haru chat`, slash commands switch targets mid-session:
|
|
46
|
+
|
|
47
|
+
| REPL command | Purpose |
|
|
48
|
+
| ---------------- | ---------------------------------------------------------- |
|
|
49
|
+
| `/help` | Show REPL commands |
|
|
50
|
+
| `/model` | List configured models (default and active marked) |
|
|
51
|
+
| `/model <name>` | Switch the default agent to that model (resets the chat) |
|
|
52
|
+
| `/agent` | List configured agents |
|
|
53
|
+
| `/agent <name>` | Switch to that agent (resets the chat) |
|
|
54
|
+
|
|
55
|
+
## Quickstart
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
haru config init # writes ~/.config/haru (asks for your SSO start URL)
|
|
59
|
+
export HARU_AWS_ACCOUNT_ID=<your AWS account id>
|
|
60
|
+
haru login
|
|
61
|
+
haru run "Summarise the Converse API in two sentences."
|
|
62
|
+
haru chat --agent supervisor --session-id demo
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Authentication uses the same PKCE authorization-code flow as `aws sso login`,
|
|
66
|
+
caching tokens in the botocore-compatible schema under `~/.aws/sso/cache`
|
|
67
|
+
(0600) so standard AWS tooling can consume and refresh the same cache. Access
|
|
68
|
+
tokens are refreshed automatically; when a fresh login is needed, commands say
|
|
69
|
+
so plainly.
|
|
70
|
+
|
|
71
|
+
## Sampling controls
|
|
72
|
+
|
|
73
|
+
haru-cli exposes `temperature`, `top_p`, `top_k`, and `seed` on **three
|
|
74
|
+
configuration surfaces** — a capability Kiro CLI does not offer on any of its
|
|
75
|
+
configuration surfaces (its v2/v3 agent formats and `cli.json` define no
|
|
76
|
+
sampling fields):
|
|
77
|
+
|
|
78
|
+
| Surface | Where | Example |
|
|
79
|
+
| ------------------------ | ---------------------------------------------- | ----------------------------------------- |
|
|
80
|
+
| Model catalogue | `models.yaml`, per model entry | `temperature: 0.2`, `top_k: 50` |
|
|
81
|
+
| Agent profile | `agents.yaml`, per agent `sampling:` block | `sampling: {temperature: 0.1}` |
|
|
82
|
+
| CLI / REPL | `--temperature/--top-p/--top-k/--seed` flags on `run` and `chat`; `/sampling` in the REPL | `haru run "..." --top-k 20` |
|
|
83
|
+
|
|
84
|
+
Precedence: **CLI/REPL override → agent `sampling:` block → model entry**,
|
|
85
|
+
merged per-field. Unset fields are omitted from requests entirely, keeping
|
|
86
|
+
the provider's defaults. In the chat REPL, `/sampling` shows the model
|
|
87
|
+
default, session override, and effective value for each field;
|
|
88
|
+
`/sampling temperature=0.2 top_k=50` applies overrides and `/sampling reset`
|
|
89
|
+
clears them.
|
|
90
|
+
|
|
91
|
+
Model compatibility (AWS-side behaviour, not a haru limitation):
|
|
92
|
+
|
|
93
|
+
- **Claude 5-series and Opus 4.7+** (Sonnet 5, Opus 5, Opus 4.8) reject
|
|
94
|
+
non-default `temperature`/`top_p`/`top_k` with an HTTP 400 — leave them
|
|
95
|
+
unset on those entries (the shipped config does).
|
|
96
|
+
- **Claude Haiku 4.5, Sonnet 4.5, and older Claude models**, plus
|
|
97
|
+
non-Anthropic Bedrock models, accept them.
|
|
98
|
+
- `seed` is forwarded via Converse `additionalModelRequestFields` for Bedrock
|
|
99
|
+
models that support it; **no Claude model accepts a seed today**, so do not
|
|
100
|
+
expect determinism from Claude — the field exists for models that honour it.
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
Declarative YAML under `config/` — models, agents, orchestration
|
|
105
|
+
(supervisor/swarm/graph), MCP servers, guardrails, sessions, sampling,
|
|
106
|
+
logging, and OpenTelemetry. No secrets in YAML: values reference environment
|
|
107
|
+
variables with `${env:VAR}`. See [docs/configuration.md](docs/configuration.md).
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
All quality gates must pass before a change is considered done:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run ruff check
|
|
115
|
+
uv run ruff format
|
|
116
|
+
uv run mypy src
|
|
117
|
+
uv run pytest
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Coverage is gated at 90% (`--cov-fail-under=90`). CI runs the full gate set on
|
|
121
|
+
Python 3.13 and 3.14; releases are built with `uv build` and published to PyPI
|
|
122
|
+
via Trusted Publishing on `v*` tags.
|
|
123
|
+
|
|
124
|
+
Install the pre-commit hooks once per clone:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv tool run pre-commit install
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Project layout
|
|
131
|
+
|
|
132
|
+
- `src/haru/` — the package (auth, config, models, agents, tools, steering, sessions, observability, commands)
|
|
133
|
+
- `config/` — declarative YAML configuration and steering prompts
|
|
134
|
+
- `tests/unit`, `tests/integration` — pytest suites (coverage gate: 90%)
|
|
135
|
+
- `.kiro/steering/` — authoritative engineering steering documents
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -28,3 +28,10 @@ orchestration:
|
|
|
28
28
|
edges:
|
|
29
29
|
- from: research
|
|
30
30
|
to: write
|
|
31
|
+
# Per-agent sampling overrides beat the model entry's values and are in turn
|
|
32
|
+
# beaten by CLI/REPL overrides. Example:
|
|
33
|
+
# researcher:
|
|
34
|
+
# model: haiku-4-5
|
|
35
|
+
# sampling:
|
|
36
|
+
# temperature: 0.1
|
|
37
|
+
# top_k: 20
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Sampling fields (temperature, top_p, top_k, seed) are optional; unset
|
|
2
|
+
# fields are omitted from requests, keeping the provider's defaults.
|
|
3
|
+
# Claude 5-series and Opus 4.7+ models reject non-default temperature/top_p/
|
|
4
|
+
# top_k with an HTTP 400, so leave them unset on those entries. seed is
|
|
5
|
+
# passed through to models that support it (no Claude model does today).
|
|
6
|
+
default_model: sonnet-5
|
|
7
|
+
models:
|
|
8
|
+
opus-5:
|
|
9
|
+
model_id: us.anthropic.claude-opus-5
|
|
10
|
+
region: us-east-1
|
|
11
|
+
max_tokens: 8192
|
|
12
|
+
streaming: true
|
|
13
|
+
sonnet-5:
|
|
14
|
+
model_id: us.anthropic.claude-sonnet-5
|
|
15
|
+
region: us-east-1
|
|
16
|
+
max_tokens: 8192
|
|
17
|
+
streaming: true
|
|
18
|
+
opus-4-8:
|
|
19
|
+
model_id: us.anthropic.claude-opus-4-8
|
|
20
|
+
region: us-east-1
|
|
21
|
+
max_tokens: 8192
|
|
22
|
+
streaming: true
|
|
23
|
+
# Haiku 4.5 accepts sampling parameters:
|
|
24
|
+
haiku-4-5:
|
|
25
|
+
model_id: us.anthropic.claude-haiku-4-5
|
|
26
|
+
region: us-east-1
|
|
27
|
+
max_tokens: 4096
|
|
28
|
+
temperature: 0.2
|
|
29
|
+
top_p: 0.9
|
|
30
|
+
top_k: 50
|
|
31
|
+
streaming: true
|
|
@@ -38,6 +38,18 @@ Bare Bedrock model ids receive the `us.` geographic inference-profile prefix
|
|
|
38
38
|
by default. Explicit prefixes (`eu.`, `au.`, `global.`, ...) and full ARNs
|
|
39
39
|
pass through as configured — writing one into YAML is the approval surface.
|
|
40
40
|
|
|
41
|
+
## Sampling
|
|
42
|
+
|
|
43
|
+
`temperature`, `top_p`, `top_k`, and `seed` are optional on every model entry
|
|
44
|
+
and may also be set per agent (a `sampling:` block) or per invocation
|
|
45
|
+
(`--temperature/--top-p/--top-k/--seed`, or `/sampling` inside `haru chat`).
|
|
46
|
+
Precedence is CLI/REPL → agent → model, merged per-field; unset fields are
|
|
47
|
+
omitted from requests. `temperature`/`top_p` map to the Converse inference
|
|
48
|
+
configuration; `top_k` and `seed` travel via
|
|
49
|
+
`additionalModelRequestFields`. Claude 5-series and Opus 4.7+ models reject
|
|
50
|
+
non-default sampling values (HTTP 400), and no Claude model honours `seed` —
|
|
51
|
+
use these fields with models that accept them (for example Haiku 4.5).
|
|
52
|
+
|
|
41
53
|
## Sessions
|
|
42
54
|
|
|
43
55
|
Conversation persistence is configured in the base file:
|
|
@@ -15,9 +15,9 @@ from strands.models import BedrockModel
|
|
|
15
15
|
from strands.session.session_manager import SessionManager
|
|
16
16
|
from strands.tools.mcp import MCPClient
|
|
17
17
|
|
|
18
|
-
from haru.config.schema import HaruConfig
|
|
18
|
+
from haru.config.schema import HaruConfig, SamplingConfig
|
|
19
19
|
from haru.errors import ConfigError
|
|
20
|
-
from haru.models.bedrock import build_model, get_model_config
|
|
20
|
+
from haru.models.bedrock import build_model, get_model_config, merge_sampling
|
|
21
21
|
from haru.steering.prompts import DEFAULT_PROMPTS_ROOT, load_prompts, resolve_prompt
|
|
22
22
|
from haru.tools.mcp import collect_tools
|
|
23
23
|
|
|
@@ -30,16 +30,33 @@ def build_agent( # noqa: PLR0913 - keyword-only wiring points, all optional
|
|
|
30
30
|
prompts_root: Path | None = None,
|
|
31
31
|
mcp_clients: Mapping[str, MCPClient] | None = None,
|
|
32
32
|
session_manager: SessionManager | None = None,
|
|
33
|
+
model_name: str | None = None,
|
|
34
|
+
sampling: SamplingConfig | None = None,
|
|
33
35
|
) -> Agent:
|
|
34
36
|
"""Build the named agent from configuration (default model when unnamed).
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
``model_name`` overrides the model for the default (unnamed) agent only;
|
|
39
|
+
named agents pin their own model. ``sampling`` overrides sampling fields
|
|
40
|
+
(precedence: this override, then the agent's ``sampling`` block, then the
|
|
41
|
+
model entry). Raises ConfigError for unknown agents, prompt references,
|
|
42
|
+
tools, or an invalid override.
|
|
37
43
|
"""
|
|
38
44
|
if agent_name is None:
|
|
39
|
-
|
|
45
|
+
model_cfg = get_model_config(config, model_name)
|
|
46
|
+
model = build_model(model_cfg, session, guardrails=config.guardrails, sampling=sampling)
|
|
40
47
|
return Agent(model=model, session_manager=session_manager)
|
|
48
|
+
if model_name is not None:
|
|
49
|
+
raise ConfigError(
|
|
50
|
+
f"Agent {agent_name!r} pins its own model; model overrides apply"
|
|
51
|
+
" to the default agent only"
|
|
52
|
+
)
|
|
41
53
|
model, system_prompt, tools = resolve_agent_parts(
|
|
42
|
-
config,
|
|
54
|
+
config,
|
|
55
|
+
agent_name,
|
|
56
|
+
session,
|
|
57
|
+
prompts_root=prompts_root,
|
|
58
|
+
mcp_clients=mcp_clients,
|
|
59
|
+
sampling=sampling,
|
|
43
60
|
)
|
|
44
61
|
return Agent(
|
|
45
62
|
model=model,
|
|
@@ -50,13 +67,14 @@ def build_agent( # noqa: PLR0913 - keyword-only wiring points, all optional
|
|
|
50
67
|
)
|
|
51
68
|
|
|
52
69
|
|
|
53
|
-
def resolve_agent_parts(
|
|
70
|
+
def resolve_agent_parts( # noqa: PLR0913 - keyword-only wiring points, all optional
|
|
54
71
|
config: HaruConfig,
|
|
55
72
|
agent_name: str,
|
|
56
73
|
session: boto3.Session,
|
|
57
74
|
*,
|
|
58
75
|
prompts_root: Path | None = None,
|
|
59
76
|
mcp_clients: Mapping[str, MCPClient] | None = None,
|
|
77
|
+
sampling: SamplingConfig | None = None,
|
|
60
78
|
) -> tuple[BedrockModel, str | None, list[Any]]:
|
|
61
79
|
"""Resolve an agent's model, steering prompt, and tool list from config."""
|
|
62
80
|
if config.agents is None or agent_name not in config.agents.agents:
|
|
@@ -64,7 +82,10 @@ def resolve_agent_parts(
|
|
|
64
82
|
raise ConfigError(f"Unknown agent {agent_name!r}; configured agents: {available}")
|
|
65
83
|
agent_cfg = config.agents.agents[agent_name]
|
|
66
84
|
model = build_model(
|
|
67
|
-
get_model_config(config, agent_cfg.model),
|
|
85
|
+
get_model_config(config, agent_cfg.model),
|
|
86
|
+
session,
|
|
87
|
+
guardrails=config.guardrails,
|
|
88
|
+
sampling=merge_sampling(agent_cfg.sampling, sampling),
|
|
68
89
|
)
|
|
69
90
|
|
|
70
91
|
system_prompt: str | None = None
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
|
+
from haru.commands.agents import agents
|
|
5
6
|
from haru.commands.chat import chat
|
|
7
|
+
from haru.commands.config import config
|
|
6
8
|
from haru.commands.login import login
|
|
7
9
|
from haru.commands.run import run
|
|
8
10
|
from haru.commands.session import session
|
|
@@ -14,7 +16,9 @@ def cli() -> None:
|
|
|
14
16
|
"""haru: a governed CLI for Amazon Bedrock agents."""
|
|
15
17
|
|
|
16
18
|
|
|
19
|
+
cli.add_command(agents)
|
|
17
20
|
cli.add_command(chat)
|
|
21
|
+
cli.add_command(config)
|
|
18
22
|
cli.add_command(login)
|
|
19
23
|
cli.add_command(run)
|
|
20
24
|
cli.add_command(session)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""The ``haru agents`` command: list configured agents."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from haru.config import load_config, resolve_config_path
|
|
8
|
+
from haru.errors import HaruError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command()
|
|
12
|
+
@click.option(
|
|
13
|
+
"--config",
|
|
14
|
+
"config_path",
|
|
15
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
16
|
+
default=None,
|
|
17
|
+
help="Path to haru.yaml (default: ./config/haru.yaml, then ~/.config/haru/haru.yaml).",
|
|
18
|
+
)
|
|
19
|
+
def agents(config_path: Path | None) -> None:
|
|
20
|
+
"""List configured agents (model, prompt, tools, MCP servers)."""
|
|
21
|
+
try:
|
|
22
|
+
loaded = load_config(resolve_config_path(config_path))
|
|
23
|
+
except HaruError as exc:
|
|
24
|
+
raise click.ClickException(str(exc)) from exc
|
|
25
|
+
|
|
26
|
+
if loaded.agents is None or not loaded.agents.agents:
|
|
27
|
+
click.echo("No agents configured; chat uses the default agent.")
|
|
28
|
+
return
|
|
29
|
+
for name in sorted(loaded.agents.agents):
|
|
30
|
+
entry = loaded.agents.agents[name]
|
|
31
|
+
parts = [f"model={entry.model}"]
|
|
32
|
+
if entry.system_prompt_ref:
|
|
33
|
+
parts.append(f"prompt={entry.system_prompt_ref}")
|
|
34
|
+
if entry.tools:
|
|
35
|
+
parts.append(f"tools={','.join(entry.tools)}")
|
|
36
|
+
if entry.mcp_servers:
|
|
37
|
+
parts.append(f"mcp={','.join(entry.mcp_servers)}")
|
|
38
|
+
click.echo(f"{name:<14} {' '.join(parts)}")
|
|
39
|
+
if loaded.agents.orchestration is not None:
|
|
40
|
+
click.echo(
|
|
41
|
+
f"\nDefault orchestration pattern: {loaded.agents.orchestration.default_pattern}"
|
|
42
|
+
)
|