Tags: chrstphe/GoModel
Tags
fix(anthropic): use adaptive thinking for Claude 5 models claude-opus-5 and claude-sonnet-5 were missing from adaptiveThinkingPrefixes, so reasoning requests were translated to manual thinking with budget_tokens and Anthropic rejected them with '"thinking.type.enabled" is not supported for this model. Use "thinking.type.adaptive" and "output_config.effort"'. Both dialects were affected: reasoning_effort on /v1/chat/completions and thinking on /v1/messages converge on applyReasoning. Verified against the live API for both models, streaming and non-streaming. Haiku 4.5 predates adaptive thinking and stays on manual budgets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
test(e2e): cover label-scoped budgets in the release curl matrix (ENT… …ERPILOT#597) * test(e2e): cover label-scoped budgets in the release curl matrix Adds S197-S204 for budgets scoped to a request label rather than a user_path subtree: admin CRUD and validation negatives, tagging-header enforcement with verbatim case-sensitive matching, multi-label charging, managed-key label charging, a request matched by both a user_path and a label budget, reset-one, PostgreSQL/MongoDB parity, and auth gating. S200 runs on the auth-enabled gateway rather than the main SQLite one: creating a managed key on a gateway with no GOMODEL_MASTER_KEY switches every endpoint to require bearer auth, and managed keys have no delete endpoint, so registering one there would lock the gateway down for every later scenario. Two scenarios promised more than they checked, so the assertions were brought up to the prose: S199 now checks the block names the exhausted label and *not* the healthy one, and S201 checks the user-path budget that matched the same request was charged and still has room — which is what makes it evidence that the label budget alone caused the block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(e2e): make label budget scenarios survive a failed run Review follow-ups. Budget spend is a SUM over usage rows, so deleting and recreating a budget does not forget what an earlier run charged against the same label. A scenario that exits part-way on a failed assertion skips its own cleanup, and the same-suffix rerun this file recommends for partial reruns would then meet an already-exhausted tiny budget and 429 on its first request — failing for a reason unrelated to what it tests, until someone cleared the state by hand. reset_release_budget resets a budget right after creation, so that first request behaves the same on the first run and the fifth. Confirmed by leaving an exhausted budget behind and rerunning S198 with the same suffix: exit 1 without the reset, exit 0 with it, same leftover state both times. The section preamble also claimed every scenario used tagging headers. S197 and S204 are admin-only and send no model traffic, and S200 relies on managed-key labels precisely because no tagging rule is configured, so it now says which scenarios do what. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(budgets): scope budgets to request labels (ENTERPILOT#590) * feat(budgets): scope budgets to request labels Budgets could only limit a `user_path` subtree, so an operator running several applications through one path had no way to cap their spend separately without minting a key per application (issue ENTERPILOT#319). A budget now carries a scope and a subject, mirroring the shape rate limit rules already use: - `user_path` — a path and its descendants, unchanged - `label` — every request carrying that label, matched verbatim Labels come from the tagging headers and from the managed key that authenticated the request, so a request carrying several labels is charged against every matching label budget. Label budgets are declared under `budgets.labels:` in config.yaml or through the dashboard and admin API; they have no env-var form, because labels are matched verbatim and are not env-name safe (the same rationale model rate limit rules already use). Enforcement no longer runs one SUM query per matching budget. `Store.SumSpend` takes every matching window and answers them in one scan — conditional aggregation on SQL, one `$group` on MongoDB. Timed against a 50k-row usage table: parity at one matching budget (~30ms), 96ms -> 35ms at five, 443ms -> 70ms at twenty-five. The budgets table is rekeyed to (scope, subject, period_seconds), so both SQL backends rebuild it in one transaction and MongoDB rewrites its documents in place. The rebuild also absorbs the two columns older releases added with ALTER TABLE, which removes the separate migration list. Also folds three near-identical evaluation loops in the budget service into one match/evaluate pair, moves the SQL-only path helpers out of the shared store file, and corrects the admin API examples in the budgets docs, which showed URL path parameters those endpoints never had. Part of ENTERPILOT#319 — this covers the budgeting integration the issue asked for. Label filtering and grouping in the audit log remain open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(budgets): drop the legacy MongoDB index before rewriting documents The scope migration unset `user_path` while the pre-scope unique index on (user_path, period_seconds) was still in place. A missing field indexes as null, so every migrated document sharing a period collapsed onto the key {user_path: null, period_seconds: N} and the rewrite failed with a duplicate key error — aborting startup for any MongoDB deployment holding two budgets with the same period, which is the ordinary case rather than an edge one. `internal/ratelimit` has carried the same ordering since rule scopes shipped, so it is fixed alongside. Both now drop the legacy index first, and both gain a mongotest case that seeds two pre-scope documents sharing a period and asserts they survive the upgrade. Also asserts at the server layer that request labels reach the budget checker with the user path, which is what lets a label budget match at all. Reported by Greptile and CodeRabbit on ENTERPILOT#590. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(budgets): cover the PostgreSQL label predicate in SumSpend The label branch of SumSpend is the one part written twice — json_each on SQLite, jsonb_exists on PostgreSQL — and only the SQLite half was tested, because the test opened a raw SQLite database to get a usage store. A mistake in the PostgreSQL predicate would have silently matched nothing, so label budgets would never enforce there and no test would notice. The case now runs on both backends. Verified it catches a broken predicate by corrupting the bound subject and watching the PostgreSQL subtest fail. `usage` has not moved onto sqlx, so its PostgreSQL store still takes a *pgxpool.Pool directly; sqlxtest grows an exported NewPostgresPool so a test spanning both can point them at one throwaway schema. Entry ids become fixed UUIDs derived from their readable names, since PostgreSQL types usage.id as a UUID. Reported by CodeRabbit on ENTERPILOT#590. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(dashboard): show a budget's scope on the subject itself The budget list carried the scope twice: a "(tag) label" pill sat next to the period, while the subject rendered in .budget-user-path regardless of what it actually was. The pill said nothing the subject could not say itself, and the class name was wrong for half the rows. The tag icon now prefixes the label name inside the subject, and the shared styling moves to .budget-scope-value with .budget-user-path and .budget-label as hue-only modifiers. A label takes the same per-label colour the usage log, breakdown chart, and API keys already use, so one label reads the same everywhere. The rate limit list and inspector pick up the shared base class. Also rounds the provider status toggle to 6px, matching .btn and .table-action-btn rather than the full pill it was; the switch track and thumb stay round. And the budget filter placeholder now mentions labels, which it can now match on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(admin): restrict dashboard and admin API to selected API keys (E… …NTERPILOT#584) * feat(admin): restrict dashboard and admin API to selected API keys Managed API keys now carry a dashboard_access flag (default: false). Only the master key and flagged keys can call /admin/*; other managed keys get 403 dashboard_access_denied. Model endpoints and GET /v1/usage stay open to every key, and the no-master-key lockout-recovery path is unchanged. - authkeys: DashboardAccess on AuthKey/CreateInput/AuthenticationResult, store migrations (sqlite/postgres/mongo) + UpdateDashboardAccess - admin API: dashboard_access on POST /admin/auth-keys, new PUT /admin/auth-keys/{id}/dashboard-access - server: AdminAccessMiddleware gates /admin and the legacy alias - dashboard: create-form checkbox, table column with helper text, grant/revoke row action, auth dialog reopens with a specific message when a key without access is used Closes ENTERPILOT#583 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(admin): address review findings on dashboard access gate - PUT /admin/auth-keys/{id}/dashboard-access rejects omitted/null dashboard_access with 400 instead of silently revoking - disable all dashboard-access toggles while one update is in flight - gate tests: table-driven allowed-credential matrix covering the legacy /admin/api/v1 alias - sqlite test helper asserts List returns exactly the expected keys - CLAUDE.md: scope the gate claim to admin REST endpoints; note the dashboard shell/static assets skip auth Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(authkeys): reject duplicate IDs in sqlite dashboard-access assert Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
feat(conversations): complete OpenAI-compatible lifecycle (ENTERPILOT… …#576) * feat(conversations): complete OpenAI-compatible lifecycle * fix(conversations): address persistence review findings * fix(conversations): guard fragmented stream completion * fix(conversations): preserve streamed output JSON
feat(marketing): track first-party referrals (ENTERPILOT#570) * feat(marketing): track first-party referrals * docs(readme): track documentation referrals
feat(demo): add environment-controlled demo mode (ENTERPILOT#559) * feat(demo): add environment-controlled demo mode * fix(dashboard): prevent search engine indexing * fix(demo): correct environment variable name * fix(demo): update runtime config test
chore(demo): expand synthetic dashboard data (ENTERPILOT#557) * chore(demo): expand synthetic dashboard data * chore(demo): seed routing config and playable audio
feat(mcp): publish server.json to the official MCP registry on release ( ENTERPILOT#545) * feat(mcp): publish server.json to the official MCP registry on release Lists GoModel in registry.modelcontextprotocol.io as io.github.enterpilot/gomodel — an OCI package (docker.io/enterpilot/gomodel) with streamable HTTP transport at /mcp. The Dockerfile LABEL is the registry ownership proof and must match the server.json name. A new release job authenticates via GitHub OIDC (no secrets) after docker-promote, stamps the tag version into server.json, and publishes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): harden the MCP registry publish job continue-on-error so a registry outage cannot fail the release, persist-credentials off (the job runs a third-party binary), and mcp-publisher pinned to v1.8.0 with checksum verification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
PreviousNext