OpenBMB / OpenBMB/PilotDeck

bug(router): stats.filePath is parsed as a file path but always writes stats.jsonl

Open
#251 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
4k
Forks
453
Avg merge
12h 30m
Merged PRs (30d)
46

Description

Summary

router.stats.filePath is declared as an override for the stats file path, but the parser drops it, and the collector would not honor the full filename even if it received the field.

Why it matters

A user or test can reasonably set router.stats.filePath: /tmp/custom-name.jsonl and expect stats to be written to that exact file. Today the field does not survive parsing. Separately, TokenStatsCollector treats filePath as a directory hint by taking dirname(filePath) and then always writing stats.jsonl. This can make stats data appear missing, cause tests to read the wrong file, or make multiple custom configs collide on the same default filename.

Evidence

  • src/router/config/schema.ts:58 documents filePath as an override for the default ~/.pilotdeck/router/stats.json path.
  • src/router/config/schema.ts:59 declares filePath?: string.
  • src/router/config/parseRouterConfig.ts:491 enters parseStats.
  • src/router/config/parseRouterConfig.ts:508 parses enabled.
  • src/router/config/parseRouterConfig.ts:510 parses modelPricing.
  • src/router/config/parseRouterConfig.ts:532 parses baselineModel.
  • src/router/config/parseRouterConfig.ts:538 returns { enabled, modelPricing, baselineModel }, dropping filePath.
  • src/router/stats/TokenStatsCollector.ts:71 checks config?.filePath.
  • src/router/stats/TokenStatsCollector.ts:72 uses path.dirname(config.filePath).
  • src/router/stats/TokenStatsCollector.ts:76 always sets jsonlPath to path.join(routerDir, "stats.jsonl").

Validation

Validation level: dynamic parser reproduction plus source-control-flow confirmation.

Minimal reproduction:

  1. Parse router config containing:
    • router.stats.enabled: true
    • router.stats.filePath: /tmp/custom-name.jsonl
  2. Inspect parsed router stats.
  3. Inspect collector path construction for the same field.

Key output:

  • Parser output is { enabled: true }.
  • filePath is absent from parsed stats.
  • Collector code would convert /tmp/custom-name.jsonl to directory /tmp, then write /tmp/stats.jsonl.

Boundary: the parser loss was reproduced directly. The collector behavior is confirmed by source control flow. This does not validate concurrent stats writes or migration behavior.

Expected behavior

The behavior should match the field contract. Either:

  • router.stats.filePath means a full output file path, in which case the parser should preserve it and the collector should write exactly that path; or
  • the supported override is only a directory, in which case the schema/config field should be renamed or documented as a directory setting instead of filePath.

Existing coverage checked

No matching fix was found.

Checked adjacent work:

  • PR #208 touches router runtime internals, but does not preserve router.stats.filePath in the parser or make the collector honor the full file path.

Search terms checked included router.stats.filePath, TokenStatsCollector filePath, and stats.jsonl custom.

Suggested fix

Pick one contract and make both parser and collector follow it.

Recommended contract:

  • Treat router.stats.filePath as a full file path.
  • Preserve filePath in parseStats.
  • In TokenStatsCollector, create path.dirname(filePath) but set jsonlPath to filePath itself.
  • Keep the current default path when filePath is absent.

If the intended contract is directory-only, rename the setting or update schema/docs so users are not promised a file path override.

Suggested tests

  • Parsing router.stats.filePath preserves the exact string in RouterStatsConfig.
  • TokenStatsCollector({ enabled: true, filePath: "/tmp/custom-name.jsonl" }) writes to /tmp/custom-name.jsonl, not /tmp/stats.jsonl.
  • Default behavior without filePath still writes the existing default stats file.
  • Migration logic still works when a custom full file path is configured.

Submitted with Codex.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with parseStats in src/router/config/parseRouterConfig.ts and the filePath handling in src/router/stats/TokenStatsCollector.ts. Reproduce parsing with /tmp/custom-name.jsonl, then inspect the suggested parser and collector cases, including the default path and migration behavior. Done means the chosen filePath contract is consistent and the custom and default outputs are covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.