Skip to content

Merge PR #2 (model routing + cost tracking) into alpha/v0.1.0 + Linux Docker deploy#3

Closed
Protocol-zero-0 wants to merge 6 commits into
alpha/v0.1.0from
alpha/v0.1.0-pr2-integration
Closed

Merge PR #2 (model routing + cost tracking) into alpha/v0.1.0 + Linux Docker deploy#3
Protocol-zero-0 wants to merge 6 commits into
alpha/v0.1.0from
alpha/v0.1.0-pr2-integration

Conversation

@Protocol-zero-0
Copy link
Copy Markdown
Contributor

Summary

Completes HelloAnner's PR #2 (model routing + per-tier cost tracking) and lands it on alpha/v0.1.0 instead of the abandoned v6-release branch, plus the minimum additions needed for a Linux-native Phase-1 demo.

Phase-1 goal (per offline alignment): given a token budget, ClawOSS runs autonomously as a demo. That's what this PR enables end-to-end.

What changed

Merged from PR #2 (fix/model-routing-and-cost-tracking)

  • Generic LLM_PROVIDER / LLM_BASE_URL / LLM_API_KEY / LLM_MODEL_COMPLEX / LLM_MODEL_SIMPLE env scheme replaces hardcoded minimax/MiniMax-M2.7. Alpha's CLAWOSS_* vars preserved as optional legacy overrides.
  • Per-tier pricing (INPUT_COST_PER_M_COMPLEX / _SIMPLE, flat fallback) flows through post-tool.sh, handler.ts, dashboard-sync.sh.
  • heartbeat.every=5m and __LLM_*__ template placeholders in config/openclaw.json; alpha's acp.defaultAgent=codex, loopDetection, logging.level preserved.
  • restart.sh now exports LLM_* and MODEL_TOKEN_BUDGETS into the deployed config env block alongside alpha's CLAWOSS_ROOT / CLAWOSS_RECORD_*.
  • Dashboard: /api/agent/llm-health probe, model-budget-banner, cost-models lookup, llm-error-banner merged; env-driven model display in header/gateway.
  • .env.example rewritten: LLM_* primary scheme + legacy CLAWOSS_* section + full Provider Quick Reference (Gemini, Mistral, DeepSeek, MiniMax, Moonshot, GLM).

Added for Linux demo (new in this PR, not in original #2)

  • deploy/docker/Dockerfile + docker-compose.yml + entrypoint.sh. Single-container Linux deployment that installs openclaw via npm, validates required env vars up-front, runs openclaw gateway run under tini as PID 1. Named volume persists ~/.openclaw/ state across restarts. README.md documents how this relates to the existing docker/ (autonomy backend API/worker/reflection).
  • scripts/restart.sh — prints explicit [SKIP] for launchd/plist step on non-macOS, emits a platform banner on boot, warns loudly if no launchd AND no systemd is available. Closes the silent-fail-on-Linux footgun.
  • .github/workflows/smoke.yml (new) — bash -n over every shell script, sourceability check on .env.example, validate-config.mjs, and docker build of the new image. Triggers on PRs to main AND alpha/**.
  • scripts/validate-config.mjs + .github/workflows/validate.ymlopenclaw.json now contains __PLACEHOLDER__ tokens substituted at deploy time. Validator runs the same sed-style substitution before JSON.parse so CI doesn't go red on the template shape. Validate workflow also triggers on alpha/**.

Conflict resolution notes

Merge was not a clean replay — 10 files conflicted. Each resolution preserved both branches' features:

File Kept from alpha Kept from PR #2
.env.example CLAWOSS_RECORD_DECISIONS/OUTCOMES, CLAWOSS_ROOT, autonomy-backend hints LLM_* primary scheme, MODEL_TOKEN_BUDGETS, Provider Quick Reference
config/openclaw.json acp.defaultAgent, loopDetection, logging.level, external-controller heartbeat prompt LLM_* placeholders, heartbeat.every=5m, maxConcurrent=14
restart.sh CLAWOSS_ROOT/RECORD_* env forwarding, systemd user-unit path LLM_*/BUDGET_USD_TOTAL/MODEL_TOKEN_BUDGETS env forwarding, per-tier pricing
dashboard/app/api/connection-status/route.ts response.connection/response.pipeline shape for demo-seed mode llm health probe block
handler.ts (nothing kept — PR #2 scheme is strictly newer) PRICING per-tier object, tier-aware metrics loop, modelTierForSession

Known limits

  • Token counting is still an estimate (Math.ceil(paramStr.length / 4)), not real upstream usage. The honest fix is wiring OpenClaw's native usage telemetry through — out of scope for this PR.
  • Docker image is validated by docker build only in CI — no end-to-end run. Running the container requires real LLM + GitHub credentials, which we're not injecting into CI.
  • validate.yml openclaw.json check relaxed — was an inline JSON.parse, now delegates the post-substitution parse to validate-config.mjs. Net effect: still validated, just in one place instead of two.
  • The Notion summary attached to PR fix: model routing bugs, per-model cost tracking, and quickstart doc #2 partly misrepresents the root cause (it blamed an env-var-in-jq bug that didn't exist — the pre-fix state had "kimi-coding/k2p5" hardcoded). Landing this PR supersedes that analysis; keeping the note here so future readers don't re-chase the wrong lead.

Test plan

  • bash -n passes for all shell scripts (local + smoke.yml CI)
  • node scripts/validate-config.mjs passes
  • openclaw.json parses valid JSON after sed substitution
  • handler.ts has no stale refs to removed DEFAULT_MODEL / accumulatedInputTokens / INPUT_COST_PER_TOKEN
  • CI green on this PR (verify after push)
  • docker compose -f deploy/docker/docker-compose.yml up --build boots the gateway on a real Linux host with a real .env (operator verification — out of CI)
  • One end-to-end autonomous run with BUDGET_USD_TOTAL=5 to confirm the token-budget cutoff fires and the dashboard banner appears (operator verification — out of CI)

🤖 Generated with Claude Code

HelloAnner and others added 6 commits April 11, 2026 14:35
- Fix jq syntax error in post-tool.sh that silently broke metrics reporting
- Split cost tracking: complex and simple models now use independent pricing
  variables (__INPUT_COST_PER_M_COMPLEX/SIMPLE__) in openclaw.json
- Add per-model cost variable substitution in restart.sh
- Fix budget exhaustion check (>= to >) to avoid premature shutdown
- Fix metrics overview cost estimation to respect per-model pricing
- Make GITHUB_USERNAME configurable in dashboard-reporter and github sync
- Add docs/quickstart.md with setup, model switching, budget, and dashboard guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a MODEL_TOKEN_BUDGETS config (env var + dashboard settings) that caps
cumulative input+output token usage per model, matched by bare model name so
the same model served by multiple providers (e.g. z-ai/glm-4.6 and
openrouter/glm-4.6) shares a single counter. When a model exceeds its cap, the
health-check endpoint emits a MODEL TOKEN BUDGET EXHAUSTED directive picked up
by the heartbeat loop, and a non-dismissible red banner appears at the top of
every dashboard page.

- Add bareModelName() helper for cross-provider model matching
- Extend /api/agent/health-check with per-model aggregation, directive
  injection, and a new modelBudgets response field (exhausted/usage/caps)
- Add ModelBudgetBanner client component, mounted globally in app/layout.tsx
- Accept and normalize modelTokenBudgets in /api/settings PUT
- Wire NEXT_PUBLIC_LLM_MODEL_COMPLEX into the header model label
- Document MODEL_TOKEN_BUDGETS semantics in docs/model-routing.md and
  .env.example, emphasizing bare-name matching across providers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dashboard's connection state only reflects whether the
`dashboard-reporter` hook has fired heartbeats, and that hook only fires on
`agent_end`. In continuous mode the main session never ends (stopReason=toolUse
loop), so a running agent whose first LLM call is failing would appear
`disconnected` even though the container is perfectly alive — and an agent
hitting repeated upstream 401/quota errors would be indistinguishable from
one whose container has died.

Add a separate LLM health signal derived directly from the openclaw session
jsonl, and surface it as its own top-of-page banner so the two failure modes
are visually distinct.

- Add /api/agent/llm-health — tails latest session jsonl, walks back from
  the most recent assistant message to classify the LLM as ok/errored/unknown
  and report the most recent error message and timestamps
- Embed the LLM health block inside /api/connection-status so existing
  consumers get both dimensions in a single request
- Add LlmErrorBanner client component, mounted alongside ModelBudgetBanner
  in the root layout. Renders only when llm.state === "errored", showing the
  upstream error text and "last ok/fail" relative timestamps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integrates HelloAnner's fix/model-routing-and-cost-tracking branch with
alpha/v0.1.0's extra features preserved:

  - Generic LLM_PROVIDER / LLM_BASE_URL / LLM_API_KEY / LLM_MODEL_* env scheme
    supersedes hardcoded minimax/MiniMax-M2.7 defaults. CLAWOSS_PRIMARY_MODEL
    etc. kept as optional legacy overrides.
  - Per-tier pricing (INPUT_COST_PER_M_COMPLEX / _SIMPLE, with flat fallback)
    flows through post-tool.sh, handler.ts, and dashboard-sync.sh.
  - heartbeat.every=5m and LLM_* placeholders land in config/openclaw.json;
    acp.defaultAgent=codex, loopDetection, logging.level from alpha preserved.
  - restart.sh now also exports LLM_* and MODEL_TOKEN_BUDGETS into the
    deployed config env block, alongside alpha's CLAWOSS_ROOT / RECORD_* vars.
  - Dashboard: llm-health probe, model-budget banner, cost-models lookup,
    llm-error banner all merged; env-driven model display in header/gateway.
  - .env.example rewritten with LLM_* primary + legacy CLAWOSS_* section and
    full Provider Quick Reference block (Gemini, Mistral, DeepSeek, MiniMax,
    Moonshot, GLM).

Not a clean replay: 10 conflict files resolved by hand. See the PR body for
the dimension-by-dimension integration notes.
Completes the Phase-1 demo goal: "given a token budget, ClawOSS runs
autonomously on a Linux host."

- deploy/docker/: Dockerfile + docker-compose.yml + entrypoint.sh.
  Single-container deployment that installs openclaw via npm, validates
  required env vars up-front (fails loudly before burning tokens), and
  execs `openclaw gateway run` as PID 1 under tini. Named volume
  persists ~/.openclaw state across restarts. README documents how this
  relates to the existing docker/ (autonomy backend) setup.

- scripts/restart.sh: prints an explicit [SKIP] for the launchd/plist
  step on non-macOS and emits a platform banner at boot. Linux hosts
  with systemd get a clear "using systemd user units" message; hosts
  without launchd or systemd get a WARN that the gateway will fall back
  to an unmanaged background process.

- .github/workflows/smoke.yml (new): bash -n over every shell script,
  sourceability check on .env.example, validate-config.mjs, and
  `docker build` of the new deploy/docker image. Triggers on PRs to
  main AND alpha/**, so alpha branch is now gate-protected.

- scripts/validate-config.mjs + .github/workflows/validate.yml:
  openclaw.json now contains __PLACEHOLDER__ tokens substituted at
  deploy time. Validator runs the same sed substitution before
  JSON.parse so CI doesn't go red on the template shape. Validate
  workflow also triggers on alpha/** now.
Copilot AI review requested due to automatic review settings April 17, 2026 10:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR merges the prior “model routing + per-tier cost tracking” work onto alpha/v0.1.0, and adds a Linux-native Docker deployment path plus CI smoke/validation to support an end-to-end “token budget → autonomous run” Phase-1 demo.

Changes:

  • Introduces env-driven model routing (LLM_*) and per-tier pricing variables, flowing through hook telemetry, dashboard UI, and deploy scripts.
  • Adds Linux Docker deployment (single container running openclaw gateway run) and new CI workflows for shell syntax, config validation, and Docker build.
  • Adds dashboard budget enforcement features (total USD cap + per-model token caps), LLM health probing, and model/budget banners.

Reviewed changes

Copilot reviewed 31 out of 32 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
workspace/hooks/dashboard-reporter/post-tool.sh Updates default model metadata to follow env-driven routing.
workspace/hooks/dashboard-reporter/handler.ts Implements per-tier token/cost tracking and env-driven model IDs in telemetry.
scripts/validate-config.mjs Validates openclaw.json after placeholder substitution for CI.
scripts/start.sh Updates agent model defaulting to support LLM_* routing.
scripts/restart.sh Adds Linux-aware messaging + deploy-time placeholder substitution + env forwarding.
scripts/dashboard-sync.sh Updates model default fallback to prefer LLM_*/legacy env vars.
docs/quickstart.md Adds setup guide covering LLM_*, budget controls, and dashboard usage.
docs/model-routing.md Adds detailed model routing + pricing + budget documentation.
deploy/docker/entrypoint.sh Validates required env vars, deploys substituted config, and starts gateway.
deploy/docker/docker-compose.yml Compose-based single-container agent deployment with persisted state.
deploy/docker/README.md Documents Linux Docker deployment and relationship to existing docker setup.
deploy/docker/Dockerfile Builds the agent image with openclaw CLI and runtime dependencies.
dashboard/lib/types.ts Extends settings types for total budget + per-model token caps + model display.
dashboard/lib/github.ts Uses GITHUB_USERNAME as fallback for PR discovery username.
dashboard/lib/cost-models.ts Expands cost model registry; adds env-based fallback and model normalization.
dashboard/components/overview/metric-cards.tsx Displays model name in cost card using NEXT_PUBLIC_LLM_MODEL_SIMPLE.
dashboard/components/live/gateway-status.tsx Displays model using NEXT_PUBLIC_LLM_* vars.
dashboard/components/layout/model-budget-banner.tsx Adds UI banner for exhausted per-model token budgets.
dashboard/components/layout/llm-error-banner.tsx Adds UI banner for upstream LLM error state.
dashboard/app/page.tsx Shows model/user in header/pipeline bar using env vars.
dashboard/app/layout.tsx Adds global budget + LLM error banners to layout.
dashboard/app/api/settings/route.ts Normalizes and persists modelTokenBudgets in settings.
dashboard/app/api/metrics/overview/route.ts Improves fallback cost estimation using env-configured pricing.
dashboard/app/api/connection-status/route.ts Adds llm health block by probing /api/agent/llm-health.
dashboard/app/api/agent/llm-health/route.ts New endpoint that infers recent LLM success/failure from session jsonl.
dashboard/app/api/agent/health-check/route.ts Enforces USD budget + per-model token caps and returns directives/budget state.
config/openclaw.json Converts to a placeholder-driven template for LLM_* routing and tiered pricing.
CLAUDE.md Updates model configuration documentation to reference LLM_* scheme.
.github/workflows/validate.yml Adjusts validation to account for templated openclaw.json.
.github/workflows/smoke.yml Adds smoke workflow: bash syntax, .env.example sourcing, config validation, docker build.
.env.example Rewrites env scheme around LLM_*, pricing, budgets, and provider quick reference.
Comments suppressed due to low confidence (2)

config/openclaw.json:58

  • The heartbeat prompt hardcodes an absolute path (/home/ubuntu/projects/.../workspace/HEARTBEAT.md). This will be wrong for most installs (including Docker, where workspace is /app/workspace). Since this file already uses WORKSPACE_PATH placeholders, consider templating this path as well (e.g., WORKSPACE_PATH/HEARTBEAT.md) so the prompt remains correct after substitution.
          "every": "5m",
          "model": "__LLM_PROVIDER__/__LLM_MODEL_SIMPLE__",
          "session": "main",
          "target": "none",
          "prompt": "External-controller mode. Read /home/ubuntu/projects/codex/ClawOSS/workspace/HEARTBEAT.md and follow the current prompt goal and output contract.",
          "lightContext": true

scripts/start.sh:19

  • AGENT_MODEL is computed before sourcing .env, so LLM_PROVIDER / LLM_MODEL_SIMPLE set in .env won't affect the model used to register the agent. Move the .env sourcing block above the AGENT_MODEL assignment (or recompute AGENT_MODEL after sourcing) so the documented env-driven routing actually takes effect.
PROJECT_DIR="$(clawoss_resolve_project_dir "$0")"
AGENT_ID="clawoss"
WORKSPACE_DIR="$(clawoss_resolve_workspace_dir "$0")"
AGENT_MODEL="${CLAWOSS_MODEL:-${CLAWOSS_AGENT_MODEL:-${CLAWOSS_PRIMARY_MODEL:-${CLAWOSS_DEFAULT_MODEL:-${LLM_PROVIDER:-anthropic}/${LLM_MODEL_SIMPLE:-claude-sonnet-4-6}}}}}"

if [ -f "$PROJECT_DIR/.env" ]; then
    set -a
    # shellcheck disable=SC1090
    source "$PROJECT_DIR/.env"
    set +a

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 101 to 104
<div className="flex justify-between">
<span className="stat-label">Model</span>
<span className="text-foreground/60">minimax/MiniMax-M2.7</span>
<span className="text-foreground/60">{process.env.NEXT_PUBLIC_LLM_PROVIDER || "anthropic"}/{process.env.NEXT_PUBLIC_LLM_MODEL_COMPLEX || "claude-opus-4-6"}</span>
</div>
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component now displays the model from NEXT_PUBLIC_LLM_* env vars, but other related gateway stats (heartbeat interval/next heartbeat estimate, max concurrency) are still hard-coded elsewhere in this component. With the PR changing heartbeat.every to 5m and subagents.maxConcurrent to 14, the UI will be misleading unless those values are updated/wired to the same source of truth.

Copilot uses AI. Check for mistakes.
Comment thread dashboard/app/page.tsx
Comment on lines +224 to 227
<span>source <span className="text-foreground/45">github/{process.env.GITHUB_USERNAME || "BillionClaw"}</span></span>
<span className="text-muted-foreground/10">|</span>
<span>model <span className="text-foreground/45">{process.env.NEXT_PUBLIC_LLM_MODEL_COMPLEX || "claude-opus-4-6"}</span></span>
<span>cost <span className="text-foreground/45">$0.60/$3.00/M</span></span>
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pipeline telemetry bar now shows the model from env, but the displayed cost is still hard-coded as "$0.60/$3.00/M". This will be incorrect for most providers/models and can mislead budget expectations; consider deriving it from env pricing variables or the cost-model registry, or omit it if unknown.

Copilot uses AI. Check for mistakes.
Comment on lines +134 to +141
const inputCostComplex = parseFloat(process.env.INPUT_COST_PER_M_COMPLEX || process.env.INPUT_COST_PER_M || "3.0");
const outputCostComplex = parseFloat(process.env.OUTPUT_COST_PER_M_COMPLEX || process.env.OUTPUT_COST_PER_M || "15.0");
const inputCostSimple = parseFloat(process.env.INPUT_COST_PER_M_SIMPLE || process.env.INPUT_COST_PER_M || "3.0");
const outputCostSimple = parseFloat(process.env.OUTPUT_COST_PER_M_SIMPLE || process.env.OUTPUT_COST_PER_M || "15.0");
// Weighted average: ~40% complex (sub-agents) + 60% simple (orchestrator)
const avgInputCostPerM = inputCostComplex * 0.4 + inputCostSimple * 0.6;
const avgOutputCostPerM = outputCostComplex * 0.4 + outputCostSimple * 0.6;
costToday = (inputTokensToday * avgInputCostPerM + outputTokensToday * avgOutputCostPerM) / 1_000_000;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseFloat(...) results here are used directly in arithmetic; if any pricing env var is set to a non-numeric value, costToday becomes NaN and the API may return invalid numeric fields. Add Number.isFinite guards and fall back to defaults when parsing fails.

Copilot uses AI. Check for mistakes.
Comment thread docs/quickstart.md
Comment on lines +5 to +6
- Node.js 22+
- GitHub Classic Token(`ghp_*` 格式,需要 `repo` scope)
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prereq says "Node.js 22+", but CI workflows in this repo use Node 20. If Node 22 is truly required, CI should match; otherwise consider relaxing the doc to 20+ (or clarify 22+ is only for the Docker image). Also, this section says classic token needs "repo" scope; elsewhere docs mention "public_repo"—please align the required scopes.

Suggested change
- Node.js 22+
- GitHub Classic Token(`ghp_*` 格式,需要 `repo` scope)
- Node.js 20+
- GitHub Classic Token(`ghp_*` 格式;公开仓库需要 `public_repo` scope,如需访问私有仓库则使用 `repo`

Copilot uses AI. Check for mistakes.
Comment thread deploy/docker/Dockerfile
Comment on lines +33 to +35
# Install openclaw CLI globally. Pinned to a known-good range; operators can
# override with --build-arg OPENCLAW_VERSION=x.y.z.
ARG OPENCLAW_VERSION=latest
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OPENCLAW_VERSION defaults to "latest", which makes image builds non-reproducible and contradicts the comment about being pinned to a known-good range. Consider defaulting to an explicit pinned version (or semver range) and updating the comment accordingly.

Suggested change
# Install openclaw CLI globally. Pinned to a known-good range; operators can
# override with --build-arg OPENCLAW_VERSION=x.y.z.
ARG OPENCLAW_VERSION=latest
# Install openclaw CLI globally. Defaults to a pinned known-good version;
# operators can override with --build-arg OPENCLAW_VERSION=x.y.z.
ARG OPENCLAW_VERSION=1.0.0

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +48
const content = await fs.readFile(latest, "utf8");
const lines = content.split("\n").filter(Boolean);

Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint reads the entire latest session .jsonl into memory and splits it into lines on every poll. Since the dashboard polls this regularly, large session files can cause unnecessary CPU/memory usage. Consider reading from the end of the file (tail) or streaming backwards until the last assistant event is found.

Copilot uses AI. Check for mistakes.
Comment thread docs/model-routing.md
| 主 Agent session | `LLM_MODEL_SIMPLE` | 同上 |
| 所有 Sub-agents(实现、跟进、监控) | `LLM_MODEL_COMPLEX` | 需深度理解代码、写 patch、分析 review |

Fallback:complex 失败时回退 simple。
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc states "Fallback:complex 失败时回退 simple", but config/openclaw.json sets primary to the simple model and fallback to the complex model. Please clarify the intended fallback direction here (and/or adjust config) so the routing rules match the actual OpenClaw model config.

Suggested change
Fallback:complex 失败时回退 simple
Fallback:simple 失败时回退 complex

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +61
.replace(/__INPUT_COST_PER_M__/g, "3.0")
.replace(/__OUTPUT_COST_PER_M__/g, "15.0")
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate-config.mjs still substitutes INPUT_COST_PER_M / OUTPUT_COST_PER_M, but config/openclaw.json no longer contains these placeholders. Removing unused substitutions will reduce confusion and keep the validator aligned with the actual template surface.

Suggested change
.replace(/__INPUT_COST_PER_M__/g, "3.0")
.replace(/__OUTPUT_COST_PER_M__/g, "15.0")

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +27
const FALLBACK_IN = process.env.INPUT_COST_PER_M || "3.0";
const FALLBACK_OUT = process.env.OUTPUT_COST_PER_M || "15.0";

const PRICING = {
complex: {
input: parseFloat(process.env.INPUT_COST_PER_M_COMPLEX || FALLBACK_IN) / 1_000_000,
output: parseFloat(process.env.OUTPUT_COST_PER_M_COMPLEX || FALLBACK_OUT) / 1_000_000,
},
simple: {
input: parseFloat(process.env.INPUT_COST_PER_M_SIMPLE || FALLBACK_IN) / 1_000_000,
output: parseFloat(process.env.OUTPUT_COST_PER_M_SIMPLE || FALLBACK_OUT) / 1_000_000,
},
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PRICING is derived via parseFloat(...) without guarding against NaN. If any of the INPUT_COST_PER_M* / OUTPUT_COST_PER_M* env vars are set but non-numeric, costUsd will become NaN and can break metric ingestion/DB writes. Consider coercing with Number(...) + Number.isFinite checks and falling back to defaults when parsing fails.

Copilot uses AI. Check for mistakes.
Comment thread config/openclaw.json
Comment on lines +77 to +88
"cost": { "input": __INPUT_COST_PER_M_COMPLEX__, "output": __OUTPUT_COST_PER_M_COMPLEX__ },
"contextWindow": __LLM_CONTEXT_WINDOW__,
"maxTokens": __LLM_MAX_TOKENS__
},
{
"id": "__LLM_MODEL_SIMPLE__",
"name": "Simple Model (Sonnet-tier)",
"reasoning": false,
"input": ["text"],
"cost": { "input": __INPUT_COST_PER_M_SIMPLE__, "output": __OUTPUT_COST_PER_M_SIMPLE__ },
"contextWindow": __LLM_CONTEXT_WINDOW__,
"maxTokens": __LLM_MAX_TOKENS__
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config/openclaw.json is no longer valid JSON until placeholders (including numeric INPUT_COST_PER_M*_ and LLM_CONTEXT_WINDOW) are substituted. scripts/setup.sh currently only replaces path placeholders, so it will deploy an invalid ~/.openclaw/openclaw.json. Either update setup.sh to perform the same full substitution as restart.sh / deploy/docker/entrypoint.sh, or change the template so it remains parseable JSON pre-substitution.

Suggested change
"cost": { "input": __INPUT_COST_PER_M_COMPLEX__, "output": __OUTPUT_COST_PER_M_COMPLEX__ },
"contextWindow": __LLM_CONTEXT_WINDOW__,
"maxTokens": __LLM_MAX_TOKENS__
},
{
"id": "__LLM_MODEL_SIMPLE__",
"name": "Simple Model (Sonnet-tier)",
"reasoning": false,
"input": ["text"],
"cost": { "input": __INPUT_COST_PER_M_SIMPLE__, "output": __OUTPUT_COST_PER_M_SIMPLE__ },
"contextWindow": __LLM_CONTEXT_WINDOW__,
"maxTokens": __LLM_MAX_TOKENS__
"cost": { "input": "__INPUT_COST_PER_M_COMPLEX__", "output": "__OUTPUT_COST_PER_M_COMPLEX__" },
"contextWindow": "__LLM_CONTEXT_WINDOW__",
"maxTokens": "__LLM_MAX_TOKENS__"
},
{
"id": "__LLM_MODEL_SIMPLE__",
"name": "Simple Model (Sonnet-tier)",
"reasoning": false,
"input": ["text"],
"cost": { "input": "__INPUT_COST_PER_M_SIMPLE__", "output": "__OUTPUT_COST_PER_M_SIMPLE__" },
"contextWindow": "__LLM_CONTEXT_WINDOW__",
"maxTokens": "__LLM_MAX_TOKENS__"

Copilot uses AI. Check for mistakes.
@Protocol-zero-0
Copy link
Copy Markdown
Contributor Author

Status check-in on this PR: CI is currently red on three fixable items —

  1. Validate Configurationworkspace/skills/oss-discover/SKILL.md exceeds the 2000-char SKILL.md size limit (it's 20.7 KB).
  2. Validate Dashboard Build — 25 ESLint errors in dashboard/ (mostly unused imports / prReviews, qualityScores, nanoid, computeQualityScore).
  3. Agent Docker image buildsuseradd --uid 1000 collides with the base image's existing UID 1000; needs to switch to 1001 (or use -u $(getent passwd ubuntu | cut -d: -f3 || echo 1001) style).

Given the alpha/v0.1.0 track has been quiet and #13 on v6-release looks like the active line, I don't want to keep this PR drifting. Happy to either (a) push a small fix commit for the three items above and let you decide, or (b) close this PR in favor of the v6-release track — whichever maintainers prefer. No rebase pings will follow.

Protocol-zero-0 added a commit that referenced this pull request May 17, 2026
… from PR #3)

Adds a single-container Linux deployment path so ClawOSS no longer
depends on macOS-only launchd/PlistBuddy assumptions:
- deploy/docker/Dockerfile + docker-compose.yml + entrypoint.sh
- deploy/docker/README.md
- scripts/validate-config.mjs (renders openclaw.json placeholders
  before JSON.parse, so CI does not red on template shape)
- .github/workflows/validate.yml

Not taken from PR #3: scripts/restart.sh Linux fallback. PR #9 already
rewrote restart.sh; the Docker entrypoint is the cleaner cross-platform
path and does not require touching restart.sh.

Refs #3 #10
@Protocol-zero-0
Copy link
Copy Markdown
Contributor Author

关闭收口说明 —

本 PR 当初的两块内容,在 v1.0 封版时做如下处理:

(A) PR #2 (model routing + cost tracking) 部分

不再单独从本 PR 吸收。原因:PR #9 用 telemetry-driven runtime 把 model routing、per-model pricing、token usage 全部重写了,绕过了 PR #2 修的那一类 bug。详细对照写在 PR #2 的关闭评论

PR #2 中一处真正的回归性 bug(budget exhaustion >=>)和 docs/quickstart.md 已单独 cherry-pick 进 v6-release:

  • commit bf36581 — budget off-by-one fix
  • commit bca65fd — quickstart.md(带 Co-Authored-By: HelloAnner)

(B) Linux Docker 部署部分

完整 cherry-pick 到 v6-release,commit 352217c:

  • deploy/docker/Dockerfile + docker-compose.yml + entrypoint.sh + README.md
  • scripts/validate-config.mjs
  • .github/workflows/validate.yml

不取 PR #3scripts/restart.sh 的 Linux fallback patch:PR #9 已经重写过 restart.sh,在 PR #9 重构语境下 PR #3 的 patch 不再适用;Docker 容器内不依赖 launchctl,已经是干净的跨平台路径。

(C) base 分支问题

本 PR base 是 alpha/v0.1.0 — 这条线已废弃,实际开发主线是 v6-release,所以即使内容全保留,也不能直接合并。

综合

(A) 的有效部分 + (B) 的 Docker 套件都已 v6-release 落地,(C) 的 base 分支问题让 PR 整体不再合并。本 PR 关闭。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants