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.
Files changed (97) hide show
  1. {haru_cli-0.1.0 → haru_cli-0.1.2}/CHANGELOG.md +32 -0
  2. {haru_cli-0.1.0 → haru_cli-0.1.2}/PKG-INFO +55 -8
  3. haru_cli-0.1.2/README.md +139 -0
  4. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/agents.yaml +7 -0
  5. haru_cli-0.1.2/config/models.yaml +31 -0
  6. {haru_cli-0.1.0 → haru_cli-0.1.2}/docs/configuration.md +12 -0
  7. {haru_cli-0.1.0 → haru_cli-0.1.2}/pyproject.toml +1 -1
  8. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/agents/factory.py +28 -7
  9. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/cli.py +4 -0
  10. haru_cli-0.1.2/src/haru/commands/agents.py +42 -0
  11. haru_cli-0.1.2/src/haru/commands/chat.py +316 -0
  12. haru_cli-0.1.2/src/haru/commands/config.py +163 -0
  13. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/login.py +13 -9
  14. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/run.py +35 -8
  15. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/session.py +9 -3
  16. haru_cli-0.1.2/src/haru/config/__init__.py +19 -0
  17. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/config/loader.py +33 -1
  18. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/config/schema.py +20 -1
  19. haru_cli-0.1.2/src/haru/config/templates/__init__.py +1 -0
  20. haru_cli-0.1.2/src/haru/config/templates/agents.yaml +37 -0
  21. haru_cli-0.1.2/src/haru/config/templates/haru.yaml +24 -0
  22. haru_cli-0.1.2/src/haru/config/templates/logging.yaml +12 -0
  23. haru_cli-0.1.2/src/haru/config/templates/mcp.yaml +15 -0
  24. haru_cli-0.1.2/src/haru/config/templates/models.yaml +31 -0
  25. haru_cli-0.1.2/src/haru/config/templates/prompts/researcher.md +14 -0
  26. haru_cli-0.1.2/src/haru/config/templates/prompts/supervisor.md +15 -0
  27. haru_cli-0.1.2/src/haru/config/templates/prompts/writer.md +12 -0
  28. haru_cli-0.1.2/src/haru/models/bedrock.py +146 -0
  29. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_agent_factory.py +36 -1
  30. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_bedrock.py +74 -3
  31. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_cli.py +173 -2
  32. haru_cli-0.1.2/tests/unit/test_config_command.py +134 -0
  33. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_config_loader.py +51 -1
  34. {haru_cli-0.1.0 → haru_cli-0.1.2}/uv.lock +1 -1
  35. haru_cli-0.1.0/README.md +0 -92
  36. haru_cli-0.1.0/config/models.yaml +0 -20
  37. haru_cli-0.1.0/src/haru/commands/chat.py +0 -102
  38. haru_cli-0.1.0/src/haru/config/__init__.py +0 -6
  39. haru_cli-0.1.0/src/haru/models/bedrock.py +0 -77
  40. {haru_cli-0.1.0 → haru_cli-0.1.2}/.github/workflows/ci.yml +0 -0
  41. {haru_cli-0.1.0 → haru_cli-0.1.2}/.github/workflows/release.yml +0 -0
  42. {haru_cli-0.1.0 → haru_cli-0.1.2}/.gitignore +0 -0
  43. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/coding-standards.md +0 -0
  44. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/git-workflow.md +0 -0
  45. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/product.md +0 -0
  46. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/security.md +0 -0
  47. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/structure.md +0 -0
  48. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/tech.md +0 -0
  49. {haru_cli-0.1.0 → haru_cli-0.1.2}/.kiro/steering/testing.md +0 -0
  50. {haru_cli-0.1.0 → haru_cli-0.1.2}/.pre-commit-config.yaml +0 -0
  51. {haru_cli-0.1.0 → haru_cli-0.1.2}/.python-version +0 -0
  52. {haru_cli-0.1.0 → haru_cli-0.1.2}/CLAUDE.md +0 -0
  53. {haru_cli-0.1.0 → haru_cli-0.1.2}/LICENSE +0 -0
  54. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/guardrails.yaml +0 -0
  55. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/haru.yaml +0 -0
  56. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/logging.yaml +0 -0
  57. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/mcp.yaml +0 -0
  58. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/prompts/researcher.md +0 -0
  59. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/prompts/supervisor.md +0 -0
  60. {haru_cli-0.1.0 → haru_cli-0.1.2}/config/prompts/writer.md +0 -0
  61. {haru_cli-0.1.0 → haru_cli-0.1.2}/renovate.json +0 -0
  62. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/__init__.py +0 -0
  63. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/__main__.py +0 -0
  64. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/agents/__init__.py +0 -0
  65. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/agents/orchestration.py +0 -0
  66. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/__init__.py +0 -0
  67. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/cache.py +0 -0
  68. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/pkce.py +0 -0
  69. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/session.py +0 -0
  70. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/auth/sso.py +0 -0
  71. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/__init__.py +0 -0
  72. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/commands/streaming.py +0 -0
  73. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/errors.py +0 -0
  74. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/models/__init__.py +0 -0
  75. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/observability/__init__.py +0 -0
  76. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/observability/guardrails.py +0 -0
  77. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/observability/telemetry.py +0 -0
  78. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/sessions/__init__.py +0 -0
  79. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/sessions/manager.py +0 -0
  80. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/steering/__init__.py +0 -0
  81. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/steering/prompts.py +0 -0
  82. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/tools/__init__.py +0 -0
  83. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/tools/mcp.py +0 -0
  84. {haru_cli-0.1.0 → haru_cli-0.1.2}/src/haru/tools/registry.py +0 -0
  85. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/conftest.py +0 -0
  86. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/integration/test_packaging.py +0 -0
  87. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_cache.py +0 -0
  88. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_guardrails.py +0 -0
  89. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_main.py +0 -0
  90. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_mcp.py +0 -0
  91. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_orchestration.py +0 -0
  92. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_pkce.py +0 -0
  93. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_prompts.py +0 -0
  94. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_session.py +0 -0
  95. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_sessions.py +0 -0
  96. {haru_cli-0.1.0 → haru_cli-0.1.2}/tests/unit/test_sso.py +0 -0
  97. {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.0
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
- Every command accepts `--config <path>` (default: `config/haru.yaml`).
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
- uv run haru login
73
- uv run haru run "Summarise the Converse API in two sentences."
74
- uv run haru chat --agent supervisor --session-id demo
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, logging, and
87
- OpenTelemetry. No secrets in YAML: values reference environment variables with
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
 
@@ -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:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "haru-cli"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "Secure, scriptable CLI for Amazon Bedrock Claude models via the AWS Strands Agents SDK."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -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
- Raises ConfigError for unknown agents, prompt references, or tools.
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
- model = build_model(get_model_config(config), session, guardrails=config.guardrails)
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, agent_name, session, prompts_root=prompts_root, mcp_clients=mcp_clients
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), session, guardrails=config.guardrails
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
+ )