Compare commits

..
4 Commits
Author SHA1 Message Date
Bellman 747e160b59 Merge pull request #3374 from ultraworkers/release/v0.1.11
release: v0.1.11
2026-11-04 22:02:52 +09:00
knowhyandknowhy 6ac59ab90b docs: document LLMTR OpenAI-compatible gateway setup (#3373)
Co-authored-by: knowhy <194148385+knowhycodata@users.noreply.github.com>
2026-11-04 13:00:45 +00:00
clawdbot 67e5783894 release: v0.1.11 2026-11-04 12:59:09 +00:00
Iain db58b190bd fix(tools): stop NotebookEdit reusing cell ids after a delete (#3371)
Inserted cells were named cell-{len+1}, which is still taken once any
cell but the last has been deleted, so a later edit by the returned id
went to the older cell instead.
2026-11-04 12:02:30 +00:00
6 changed files with 59 additions and 17 deletions

No files matched your search

+6
View File
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.11] - 2026-11-04
### Fixed
- NotebookEdit no longer reuses an existing cell id for a newly inserted cell after a cell has been deleted, so later edits by id target the intended cell (#3371)
## [0.1.10] - 2026-11-02
### Fixed
+21
View File
@@ -149,6 +149,27 @@ curl -sS "$OPENAI_BASE_URL/models" \
For local configuration, see your gateway's documentation for the correct base URL, API key format, and available model IDs. The same OPENAI_BASE_URL + OPENAI_API_KEY pattern applies to any OpenAI-compatible remote gateway.
## LLMTR
[LLMTR](https://llmtr.com) is an OpenAI-compatible gateway. Use the same client: set `OPENAI_BASE_URL` to `https://llmtr.com/v1` and `OPENAI_API_KEY` to a bearer key (`llmtr-...`).
```bash
export OPENAI_BASE_URL="https://llmtr.com/v1"
export OPENAI_API_KEY="llmtr-..."
claw --model "local/llmtr/qwen3-8-27b" prompt "Reply exactly HELLO_WORLD_123"
```
Send the catalog id unchanged. `llmtr/qwen3-8-27b` already starts with the gateway name, so that prefix stays on the wire. The `local/` hatch only selects the OpenAI-compatible transport and is removed before the request. A bare `qwen/` or `qwen-` name is a DashScope route and will not reach LLMTR, so vendor-prefixed ids need the same hatch: `local/<vendor>/<model>`.
Claw asks for up to 64,000 output tokens on these routes. LLMTR rejects a request whose `max_tokens` is above the model's output limit, so pick a model that allows at least that much. `llmtr/qwen3-8-27b` allows 262,144. Each model's limit is listed as `top_provider.max_completion_tokens` in the catalog below.
The public model list does not need a key:
```bash
curl -sS "https://llmtr.com/v1/models" \
| jq -r '.data[] | "\(.id)\t\(.top_provider.max_completion_tokens)"'
```
## Local skills install from disk
Skills are discovered from Claw skill roots such as `.claw/skills/` in a workspace and `~/.claw/skills/` for user-level installs. Legacy `.codex/skills/` roots may also be scanned for compatibility, but new local Claw projects should prefer `.claw/skills/`.
+11 -11
View File
@@ -93,7 +93,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "api"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"criterion",
"reqwest",
@@ -478,7 +478,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "claw-analog"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"api",
"clap",
@@ -498,7 +498,7 @@ dependencies = [
[[package]]
name = "claw-rag-service"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"axum 0.8.9",
"blake3",
@@ -531,7 +531,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "commands"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"plugins",
"runtime",
@@ -540,7 +540,7 @@ dependencies = [
[[package]]
name = "compat-harness"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"commands",
"runtime",
@@ -1568,7 +1568,7 @@ dependencies = [
[[package]]
name = "mock-anthropic-service"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"api",
"serde_json",
@@ -1761,7 +1761,7 @@ dependencies = [
[[package]]
name = "plugins"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"serde",
"serde_json",
@@ -2136,7 +2136,7 @@ dependencies = [
[[package]]
name = "runtime"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"glob",
"plugins",
@@ -2261,7 +2261,7 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rusty-claude-cli"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"api",
"commands",
@@ -2622,7 +2622,7 @@ dependencies = [
[[package]]
name = "telemetry"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"serde",
"serde_json",
@@ -2902,7 +2902,7 @@ dependencies = [
[[package]]
name = "tools"
version = "0.1.10"
version = "0.1.11"
dependencies = [
"api",
"aspect-core",
+1 -1
View File
@@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"
[workspace.package]
version = "0.1.10"
version = "0.1.11"
edition = "2021"
license = "MIT"
publish = false
+1 -1
View File
@@ -82,7 +82,7 @@ async fn send_message_posts_json_and_parses_response() {
);
assert_eq!(
request.headers.get("user-agent").map(String::as_str),
Some("claude-code/0.1.10")
Some("claude-code/0.1.11")
);
assert_eq!(
request.headers.get("anthropic-beta").map(String::as_str),
+19 -4
View File
@@ -5997,7 +5997,7 @@ fn execute_notebook_edit(input: NotebookEditInput) -> Result<NotebookEditOutput,
NotebookEditMode::Insert => {
let resolved_cell_type = resolved_cell_type
.ok_or_else(|| String::from("insert mode requires a cell type"))?;
let new_id = make_cell_id(cells.len());
let new_id = make_cell_id(cells);
let new_cell = build_notebook_cell(&new_id, resolved_cell_type, &new_source);
let insert_at = target_index.map_or(cells.len(), |index| index + 1);
cells.insert(insert_at, new_cell);
@@ -7022,8 +7022,11 @@ fn format_notebook_edit_mode(mode: NotebookEditMode) -> String {
}
}
fn make_cell_id(index: usize) -> String {
format!("cell-{}", index + 1)
fn make_cell_id(cells: &[Value]) -> String {
(cells.len() + 1..)
.map(|n| format!("cell-{n}"))
.find(|id| !cells.iter().any(|cell| cell["id"] == id.as_str()))
.expect("there is always a free id")
}
fn parse_skill_description(contents: &str) -> Option<String> {
@@ -9869,11 +9872,23 @@ mod tests {
assert!(deleted_output["cell_type"].is_null());
assert_eq!(deleted_output["new_source"], "");
let reinserted = execute_tool(
"NotebookEdit",
&json!({
"notebook_path": path.display().to_string(),
"new_source": "print(4)\n",
"edit_mode": "insert"
}),
)
.expect("NotebookEdit insert after delete should succeed");
let reinserted_output: serde_json::Value = serde_json::from_str(&reinserted).expect("json");
assert_eq!(reinserted_output["cell_id"], "cell-4");
let final_notebook: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(&path).expect("read notebook"))
.expect("valid notebook json");
let cells = final_notebook["cells"].as_array().expect("cells array");
assert_eq!(cells.len(), 2);
assert_eq!(cells.len(), 3);
assert_eq!(cells[0]["cell_type"], "markdown");
assert!(cells[0].get("outputs").is_none());
assert_eq!(cells[1]["cell_type"], "code");