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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ Useful optional environment variables:
| `NEXT_CACHE_MAX_MEMORY_MB` | Optional Next.js in-memory cache cap in MB. Defaults to `0`, which disables the in-memory data cache entirely; set a non-zero value where cached fetches are expected to hit. |
| `AI_GATEWAY_URL` | OpenAI-compatible chat-completions URL that powers food-photo estimates, for example `http://cliproxyapi.railway.internal:8317/v1/chat/completions`. Must be `https` unless the host is loopback or `*.railway.internal`. See `infra/cliproxyapi/`. Food-photo analysis is unavailable without it. |
| `AI_GATEWAY_API_KEY` | Backend-only bearer key for the AI gateway. Required for food-photo estimates. |
| `AI_GATEWAY_MODELS` | Optional comma-separated model list. Defaults to `gpt-5.6-luna(low),gpt-5.6-luna(medium)`; the effort suffix is translated by CLIProxyAPI into the reasoning-effort parameter. Set on the web service too (for the admin benchmark page) if customized. |
| `AI_GATEWAY_MODELS` | Optional comma-separated model list. Defaults to `gpt-6-luna(low),gpt-6-luna(medium)`; the effort suffix is translated by CLIProxyAPI into the reasoning-effort parameter. Set on the web service too (for the admin benchmark page) if customized. |
| `AI_GATEWAY_MODEL_TIMEOUT_MS` | Optional per-model attempt timeout for food-photo estimates. Defaults to `20000`, clamped to 3–30s. |
| `ENABLE_TEST_ROUTES` | Enables controlled test-only routes. Leave off in production unless you are doing a controlled test run. |
| `TEST_ROUTES_SECRET` | Required whenever `ENABLE_TEST_ROUTES=true`; send it in the `x-test-route-secret` header. |
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/legacy_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ mod benchmark_fixtures;
use benchmark_fixtures::{BENCHMARK_FIXTURES, CATEGORIES};

/// The `(effort)` suffix is CLIProxyAPI's reasoning-effort selector; the retry raises it.
const DEFAULT_FOOD_PHOTO_MODELS: &[&str] = &["gpt-5.6-luna(low)", "gpt-5.6-luna(medium)"];
const DEFAULT_FOOD_PHOTO_MODELS: &[&str] = &["gpt-6-luna(low)", "gpt-6-luna(medium)"];
/// Output budget shared by the reasoning tokens and the ~300-token JSON answer.
const FOOD_PHOTO_MAX_TOKENS: u16 = 4_000;
/// Reasoning models need headroom per attempt even at low effort.
Expand Down Expand Up @@ -484,7 +484,7 @@ async fn admin_benchmark(
StatusCode::BAD_REQUEST,
json!({
"ok": false,
"error": "Enter a model id, for example gpt-5.6-luna(low)."
"error": "Enter a model id, for example gpt-6-luna(low)."
}),
);
};
Expand Down
14 changes: 7 additions & 7 deletions apps/backend/src/legacy_api/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ async fn food_photo_succeeds_through_the_gateway() {
.await;

let result = analyze_food_photo_url_with_limits(
&gateway_test_state(&endpoint, Some("gpt-5.6-luna(low)")),
&gateway_test_state(&endpoint, Some("gpt-6-luna(low)")),
"data:image/png;base64,AA==",
"",
None,
Expand Down Expand Up @@ -404,17 +404,17 @@ fn gateway_models_come_from_config_with_luna_defaults() {
assert_eq!(
configured_food_photo_models(&config),
vec![
"gpt-5.6-luna(low)".to_string(),
"gpt-5.6-luna(medium)".to_string()
"gpt-6-luna(low)".to_string(),
"gpt-6-luna(medium)".to_string()
]
);

config.ai_gateway_models =
Some("gpt-5.6-luna(medium), gpt-5.6-terra(low)\ngpt-5.6-luna(medium)".to_string());
Some("gpt-6-luna(medium), gpt-5.6-terra(low)\ngpt-6-luna(medium)".to_string());
assert_eq!(
configured_food_photo_models(&config),
vec![
"gpt-5.6-luna(medium)".to_string(),
"gpt-6-luna(medium)".to_string(),
"gpt-5.6-terra(low)".to_string()
]
);
Expand All @@ -423,15 +423,15 @@ fn gateway_models_come_from_config_with_luna_defaults() {
#[test]
fn food_photo_request_body_omits_fields_reasoning_models_reject() {
let body = serde_json::to_value(build_food_photo_request_body(
"gpt-5.6-luna(low)",
"gpt-6-luna(low)",
"data:image/png;base64,AA==",
"",
"user-1",
false,
))
.expect("request body should serialize");

assert_eq!(body["model"], json!("gpt-5.6-luna(low)"));
assert_eq!(body["model"], json!("gpt-6-luna(low)"));
assert_eq!(body["max_tokens"], json!(4000));
// Reasoning models reject temperature pins; provider-specific routing fields would be meaningless too.
for absent in [
Expand Down
2 changes: 1 addition & 1 deletion apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ a ChatGPT/Codex subscription). Configure it on the backend environment:
```bash
AI_GATEWAY_URL=http://cliproxyapi.railway.internal:8317/v1/chat/completions
AI_GATEWAY_API_KEY=...
AI_GATEWAY_MODELS=gpt-5.6-luna(low),gpt-5.6-luna(medium)
AI_GATEWAY_MODELS=gpt-6-luna(low),gpt-6-luna(medium)
AI_GATEWAY_MODEL_TIMEOUT_MS=20000
```

Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/admin-ai-benchmark-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ export function AdminAiBenchmarkClient({
type="text"
value={model}
onChange={(event) => setModel(event.target.value)}
placeholder="gpt-5.6-luna(low)"
placeholder="gpt-6-luna(low)"
className="w-full rounded-2xl border border-[var(--color-border)] bg-[var(--color-app-bg)] px-4 py-3 font-mono text-sm text-[var(--color-ink)] outline-none transition focus:border-[var(--color-accent)]"
/>
</label>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/lib/ai-food-photo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export type AnalyzeFoodPhotoResult =

// Mirrors the backend's defaults for the AI gateway; the effort suffix is translated into the reasoning parameter.
export const DEFAULT_FOOD_PHOTO_MODELS = [
"gpt-5.6-luna(low)",
"gpt-5.6-luna(medium)",
"gpt-6-luna(low)",
"gpt-6-luna(medium)",
] as const;

function parseModelList(value: string | undefined) {
Expand Down
8 changes: 4 additions & 4 deletions apps/web/tests/unit/ai-food-photo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ describe("food photo model config", () => {
expect(getConfiguredFoodPhotoModels()).toEqual([
...DEFAULT_FOOD_PHOTO_MODELS,
]);
expect(getConfiguredFoodPhotoModel()).toBe("gpt-5.6-luna(low)");
expect(getConfiguredFoodPhotoModel()).toBe("gpt-6-luna(low)");
});

it("parses and dedupes the configured model list", () => {
process.env.AI_GATEWAY_MODELS =
"gpt-5.6-luna(medium), gpt-5.6-terra(low)\ngpt-5.6-luna(medium)";
"gpt-6-luna(medium), gpt-5.6-terra(low)\ngpt-6-luna(medium)";

expect(getConfiguredFoodPhotoModels()).toEqual([
"gpt-5.6-luna(medium)",
"gpt-6-luna(medium)",
"gpt-5.6-terra(low)",
]);
expect(getConfiguredFoodPhotoModel()).toBe("gpt-5.6-luna(medium)");
expect(getConfiguredFoodPhotoModel()).toBe("gpt-6-luna(medium)");
});

it("falls back to the defaults when the list is only separators", () => {
Expand Down
64 changes: 62 additions & 2 deletions docs/architecture/godfiles-audit.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Godfiles audit and refactoring plan

Status: analysis and plan only; no production code has been refactored.
Status: partially implemented. The September 16 update below records completed work and revises the remaining extraction plan; it does not implement further production changes.

Audit snapshot: `main`, `dev`, and `staging` at
Historical audit snapshot: `main`, `dev`, and `staging` at
`5bbfe766df371e17fa9bcd00013ccd18276dac34` on 2026-08-31. This refresh supersedes the original
`dev` snapshot at `b40658ef5343a49ce952a595489f934b59c1aaab`.

Expand Down Expand Up @@ -33,6 +33,66 @@ the code harder to follow through indirection, introduce Rust visibility/cycle p
Next.js server-action boundaries. The recommended approach therefore keeps existing public facades,
moves one cohesive responsibility at a time, and proves behavior after every move.

## Update: 2026-09-16 follow-up audit (branch `dev` at `7a354d9d`)

Status: analysis only. No production code was changed for this update; it records progress since
the previous snapshot and revises the remaining plan.

Implemented since the last snapshot (verified in the tree at `7a354d9d`):

- Inline Rust test modules moved to child files: `api/tests/mod.rs`, `auth/tests/mod.rs`,
`config/tests/mod.rs`, `legacy_api/tests/mod.rs`, `db/tests/mod.rs`, and root `tests/mod.rs`.
- `db/api_tokens.rs`, `db/weight.rs`, `db/gym.rs`, and `db/healthkit.rs` extracted from `db.rs`.
- `db/sql.rs` owns the shared `jsonb_build_object` projections and insert fragments that were the
duplicate-scan clones (plan item B1), and the pool-only `_with_executor` wrappers are gone (B2).
The complexity plan closed with zero duplicate clones and clean knip/clippy
(`complexity-reduction-results.md`).
- Web: `use-action-runner.ts`, `use-lazy-collection.ts`, and `use-copied-flash.ts` hooks exist;
`gym-shell.tsx` was split into `gym-buddies-panel.tsx` and `gym-slot-form-modal.tsx`.
- DB package: the Drizzle-era `..._ignored` varargs are removed from `backend-queries.ts` (D1);
`queries/api-tokens.test.ts` and `queries/weight.test.ts` exist.

Current line counts (physical lines, production files only, at `7a354d9d`):

| File | Lines | Change vs. previous snapshot |
| --- | ---: | --- |
| `apps/backend/src/db.rs` | 6,275 | was 11,240 |
| `apps/backend/src/legacy_api.rs` | 2,094 | was 3,499; tests moved out |
| `apps/backend/src/api.rs` | 1,362 | was 2,521; tests moved out |
| `apps/web/components/dashboard-shell.tsx` | 1,469 | roughly unchanged; still the main UI godfile |
| `apps/backend/src/db/tests/mod.rs` | 3,124 | grew into a new test monolith |
| `apps/backend/src/db/gym.rs` | 1,030 | new module; near-godfile |

### Revised guidance

The domain-only split remains the right direction. Three additions make it work:

1. Separate cross-domain workflows from plain domain persistence. `apply_template_json` (db.rs
around line 3093) loads a template, bulk-checks product access, resolves meal-group labels,
normalizes every item, then inserts transactionally; it is not template CRUD. Give such
workflows their own owner (`db/template_workflows.rs`, `db/onboarding.rs`, `db/dashboard.rs`)
so domain modules do not become mutually dependent. Dispatcher arms that still contain inline
SQL and transactions, such as the meal-group operations at db.rs lines 964-1117 and dashboard
composition at lines 1124-1135, should move their implementations into the owning domain
before the dispatch table itself is extracted into `db/rpc.rs`.
2. Keep one explicit string-dispatched `rpc_json` table. Do not introduce a registry, macro
routing, or per-domain dispatchers that each recognize the same operation names. The contract
tests that pin operation strings and argument objects stay authoritative.
3. Prevent `db/sql.rs` and `db/tests/mod.rs` from becoming the next godfiles. Move domain-specific
SQL projections and tests beside their owning modules as each extraction lands; keep only
genuinely shared projections in `sql.rs`, and keep the `SCHEMA_SQL` fixture and parity
assertion in one place.

Updated remaining sequence: extract schema readiness and shared input parsing first; then foods
and meal groups (their SQL currently sits inside dispatcher arms); then users, meals, templates,
and recipes; then admin (split into `admin/{users,barcodes,audit}.rs`) and stats; then a thin
`db/rpc.rs`. `api.rs` should split into `contract.rs`, `auth.rs`, `response.rs`, `input.rs`, and
`resources/*` while keeping the single request lifecycle (authentication, scopes, method checks,
timeout, envelope, CORS) centralized as it is today. `legacy_api.rs` should split by capability
with a shared `gateway.rs` used by both food-photo and benchmark paths, rather than nesting the
gateway under food-photo. The dashboard split stays valid; extract pure draft helpers and
render-only sections first, and keep the coupled draft/mutation state in one owner.

## Audit method and decision rule

The audit counted physical lines in every tracked source-like file, then inspected all production
Expand Down
4 changes: 2 additions & 2 deletions infra/cliproxyapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ strangers' traffic with it.
address bar and paste it back into the panel's callback field. The
panel exchanges it and writes the credential to `/data/auths`.
4. Verify: `curl -H "Authorization: Bearer $AI_GATEWAY_API_KEY"
https://<public-domain>/v1/models` should list `gpt-5.6-*` models.
https://<public-domain>/v1/models` should list `gpt-6-*` models.

Fallback if the panel flow fails: run the proxy locally with Docker
(`docker run --rm -it -p 1455:1455 -v ${PWD}/auths:/root/.cli-proxy-api
Expand All @@ -78,7 +78,7 @@ if customized) so the admin AI-benchmark page reflects the gateway models;
it does not need the API key.

`AI_GATEWAY_MODELS` is optional and defaults to
`gpt-5.6-luna(low),gpt-5.6-luna(medium)` — low effort first (~2s to first
`gpt-6-luna(low),gpt-6-luna(medium)` — low effort first (~2s to first
token), medium as the retry fallback. `AI_GATEWAY_MODEL_TIMEOUT_MS` bounds
each model attempt (defaults to 20s, clamped to 3–30s).

Expand Down
Loading