Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ backend:
type: built_in_codex # Use local Codex CLI with the built-in $imagegen skill
command: codex # Codex CLI command name or executable path
model: "" # Empty uses the Codex CLI default model; set this only when pinning a model
reasoning_effort: "" # Empty inherits Codex CLI config; set a level supported by the selected model
cwd: "" # Codex CLI working directory; empty uses the current process directory
timeout: 90s # Timeout for one Codex/imagegen invocation
delivery_dir: "" # Optional: copy generated images there; OpenClaw/TG can point this at an allowed workspace/media directory
Expand Down Expand Up @@ -216,6 +217,7 @@ Configuration fields:
- `backend.type`: generation backend type. Currently use `built_in_codex`.
- `backend.command`: Codex CLI command. Defaults to `codex`; the built-in backend currently requires this command to support `exec --json`.
- `backend.model`: model name passed to Codex CLI. If empty, the configured Codex backend chooses its default model.
- `backend.reasoning_effort`: optional Codex reasoning effort passed as a per-invocation config override. If empty, the Codex CLI configuration is inherited; supported values depend on the selected model.
- `backend.cwd`: Codex CLI working directory. If empty, the current process working directory is used. `~/` is expanded.
- `backend.timeout`: timeout for one Codex/imagegen invocation. Increase it if generation frequently times out.
- `backend.delivery_dir`: optional delivery directory. When set, generated images are copied there and the copied path is returned. OpenClaw/TG can point this at an allowed workspace/media directory.
Expand Down Expand Up @@ -263,7 +265,7 @@ Notes:

- Only local file paths are supported in this version. URLs and uploads are not supported.
- Synchronous `run` and asynchronous `submit` use the same `--image` semantics.
- The backend invokes Codex CLI as `<backend.command> exec --json --image ... -- '<prompt>'`.
- The backend invokes Codex CLI as `<backend.command> exec --json [--config model_reasoning_effort=...] --image ... -- '<prompt>'`.
- The `--` separator is required for the native Codex CLI command because variadic `--image` would otherwise consume the prompt.
- Wrappers such as `ccs codex` are not automatically compatible; if `ccs codex exec --json` reports `unknown option '--json'`, the current built-in backend cannot use it directly.

Expand Down
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ backend:
type: built_in_codex # 使用本机 Codex CLI 调用内置 $imagegen
command: codex # Codex CLI 命令名或可执行文件路径
model: "" # 为空时使用 Codex CLI 默认模型;需要固定模型时再填写
reasoning_effort: "" # 为空时继承 Codex CLI 配置;填写所选模型支持的档位
cwd: "" # Codex CLI 工作目录;为空时使用当前进程工作目录
timeout: 90s # 单次 Codex/imagegen 调用超时时间
delivery_dir: "" # 可选:生成后复制图片到该目录;OpenClaw/TG 可指向其允许发送的 workspace/media 目录
Expand Down Expand Up @@ -216,6 +217,7 @@ email:
- `backend.type`:生成后端类型;当前使用 `built_in_codex`。
- `backend.command`:Codex CLI 命令,默认 `codex`;当前内置 backend 要求该命令支持 `exec --json`。
- `backend.model`:传给 Codex CLI 的模型名;为空时由配置的 Codex backend 使用默认模型。
- `backend.reasoning_effort`:可选的 Codex 推理档位,以单次调用配置覆盖的方式传入;为空时继承 Codex CLI 配置,具体支持值取决于所选模型。
- `backend.cwd`:Codex CLI 执行工作目录;为空时使用当前进程工作目录,支持 `~/` 展开。
- `backend.timeout`:单次 Codex/imagegen 调用超时;生成经常超时时可适当调大。
- `backend.delivery_dir`:可选投递目录;配置后生成图片会先复制到该目录,再返回复制后的路径。OpenClaw/TG 可指向其允许发送的 workspace/media 目录。
Expand Down Expand Up @@ -263,7 +265,7 @@ email:

- 当前只支持本机文件路径,不支持 URL 或上传文件。
- 同步 `run` 与异步 `submit` 的 `--image` 参数语义一致。
- backend 调用 Codex CLI 时会生成 `<backend.command> exec --json --image ... -- '<prompt>'`。
- backend 调用 Codex CLI 时会生成 `<backend.command> exec --json [--config model_reasoning_effort=...] --image ... -- '<prompt>'`。
- `--` 分隔符对原生 Codex CLI 是必需的,否则 variadic `--image` 会吞掉 prompt。
- `ccs codex` 等包装命令不一定兼容;如果 `ccs codex exec --json` 报 `unknown option '--json'`,当前内置 backend 不能直接使用它。

Expand Down
1 change: 1 addition & 0 deletions cmd/imgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func main() {
generator := backend.BuiltinCodex{
Command: cfg.Backend.Command,
Model: cfg.Backend.Model,
ReasoningEffort: cfg.Backend.ReasoningEffort,
CWD: cfg.Backend.CWD,
Timeout: cfg.Backend.Timeout,
DeliveryDir: cfg.Backend.DeliveryDir,
Expand Down
1 change: 1 addition & 0 deletions configs/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ backend:
type: built_in_codex # 使用本机 Codex CLI 调用内置 $imagegen
command: codex # Codex CLI 命令名或可执行文件路径
model: "" # 为空时使用 Codex CLI 默认模型;需要固定模型时再填写
reasoning_effort: "" # 为空时继承 Codex CLI 配置;可按模型支持情况填写 medium、high、xhigh、max 等
cwd: "" # Codex CLI 工作目录;为空时使用当前进程工作目录
timeout: 300s # 单次 Codex/imagegen 调用超时时间
delivery_dir: "" # 可选:生成后复制图片到该目录;OpenClaw/TG 可指向其允许发送的 workspace/media 目录
Expand Down
2 changes: 1 addition & 1 deletion docs/openclaw-imgen-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Poll all sessions and send each successful `images[].path` as soon as that sessi
- If Telegram says `Media failed`, check from the OpenClaw runtime that the generated path exists, is readable, and is on a shared or copied filesystem.
- If Telegram downloads a JPG/photo preview instead of the PNG original, verify that the `message` call used `forceDocument: true` or `asDocument: true`.
- If a final visible message like `No extra answer from me.` appears after files are delivered, allow direct `NO_REPLY` silence in OpenClaw and ensure the final assistant reply is exactly `NO_REPLY`.
- If no image paths are returned, inspect `configs/config.yaml` for `backend.command`, `backend.model`, and `backend.cwd`, and confirm the configured backend supports Codex `exec --json`.
- If no image paths are returned, inspect `configs/config.yaml` for `backend.command`, `backend.model`, `backend.reasoning_effort`, and `backend.cwd`, and confirm the configured backend supports Codex `exec --json` and the selected model supports the configured effort.

## OpenClaw active-memory embedded run warning

Expand Down
2 changes: 1 addition & 1 deletion docs/openclaw-imgen-integration.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ final reply: NO_REPLY
- 如果 Telegram 返回 `Media failed`,从 OpenClaw 运行环境检查生成路径是否存在、可读,并确认文件系统共享或文件已复制到可访问位置。
- 如果 Telegram 下载的是 JPG/photo 预览而不是 PNG 原图,确认 `message` 调用使用了 `forceDocument: true` 或 `asDocument: true`。
- 如果文件已发送后又出现 `No extra answer from me.` 之类最终可见消息,应允许 OpenClaw direct `NO_REPLY` 静默,并确认最终 assistant 回复严格等于 `NO_REPLY`。
- 如果没有返回图片路径,检查 `configs/config.yaml` 中的 `backend.command`、`backend.model` 和 `backend.cwd`,并确认配置的 backend 支持 Codex `exec --json`。
- 如果没有返回图片路径,检查 `configs/config.yaml` 中的 `backend.command`、`backend.model`、`backend.reasoning_effort` 和 `backend.cwd`,并确认配置的 backend 支持 Codex `exec --json`、所选模型支持当前 effort

## OpenClaw active-memory 嵌入运行 warning

Expand Down
14 changes: 14 additions & 0 deletions docs/releases/v0.1.14.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Highlights

- Added an optional `backend.reasoning_effort` setting for the built-in Codex backend.
- Passed configured effort as a per-invocation Codex override so image jobs can use a project-specific effort without changing the user's global Codex default.
- Updated the example configuration, bilingual README guidance, OpenClaw troubleshooting notes, and regression coverage.

## Notes

Leave `backend.reasoning_effort` empty to inherit the Codex CLI configuration. When set, the selected Codex model must support that effort level.

## Validation

- `./scripts/ci-local.sh clean`
- GitHub PR checks: `test`, `Analyze Go`, and `CodeQL`
4 changes: 4 additions & 0 deletions internal/backend/builtin_codex.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type CommandRunner interface {
type BuiltinCodex struct {
Command string
Model string
ReasoningEffort string
CWD string
Timeout time.Duration
DeliveryDir string
Expand All @@ -35,6 +36,9 @@ type BuiltinCodex struct {

func (b BuiltinCodex) Generate(ctx context.Context, req GenerateRequest) (GenerateResult, error) {
args := []string{"exec", "--json"}
if b.ReasoningEffort != "" {
args = append(args, "--config", "model_reasoning_effort="+strconv.Quote(b.ReasoningEffort))
}
model := req.Model
if model == "" {
model = b.Model
Expand Down
10 changes: 10 additions & 0 deletions internal/backend/builtin_codex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ func TestBuiltinCodexGenerateBuildsExecArgsWithImages(t *testing.T) {
}
}

func TestBuiltinCodexGenerateBuildsExecArgsWithReasoningEffort(t *testing.T) {
runner := &recordingRunner{}
backend := BuiltinCodex{Command: "codex", Model: "gpt-5.6-terra", ReasoningEffort: "high", Runner: runner}
_, _ = backend.Generate(context.Background(), GenerateRequest{Prompt: "$imagegen draw a dragon"})
want := []string{"exec", "--json", "--config", `model_reasoning_effort="high"`, "--model", "gpt-5.6-terra", "--", "$imagegen draw a dragon"}
if !reflect.DeepEqual(runner.req.Args, want) {
t.Fatalf("args = %#v, want %#v", runner.req.Args, want)
}
}

func TestBuiltinCodexGeneratePassesCodexHome(t *testing.T) {
runner := &recordingRunner{}
codexHome := t.TempDir()
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type BackendConfig struct {
Type string `yaml:"type"`
Command string `yaml:"command"`
Model string `yaml:"model"`
ReasoningEffort string `yaml:"reasoning_effort"`
CWD string `yaml:"cwd"`
Timeout time.Duration `yaml:"timeout"`
DeliveryDir string `yaml:"delivery_dir"`
Expand Down
21 changes: 21 additions & 0 deletions internal/config/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,24 @@ func TestLoadParsesBackendDeliveryDir(t *testing.T) {
}
}

func TestLoadParsesBackendReasoningEffort(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
content := []byte(`backend:
reasoning_effort: high
`)
if err := os.WriteFile(path, content, 0o644); err != nil {
t.Fatalf("WriteFile failed: %v", err)
}

cfg, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if cfg.Backend.ReasoningEffort != "high" {
t.Fatalf("backend.reasoning_effort = %q", cfg.Backend.ReasoningEffort)
}
}

func TestLoadParsesBackendDeliveryMaxFiles(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
content := []byte(`backend:
Expand Down Expand Up @@ -300,6 +318,9 @@ func TestLoadExampleConfig(t *testing.T) {
if cfg.Backend.Command != "codex" {
t.Fatalf("backend.command = %q", cfg.Backend.Command)
}
if cfg.Backend.ReasoningEffort != "" {
t.Fatalf("backend.reasoning_effort = %q, want empty", cfg.Backend.ReasoningEffort)
}
if cfg.Backend.Timeout != 300*time.Second {
t.Fatalf("backend.timeout = %s, want 300s", cfg.Backend.Timeout)
}
Expand Down