Commit Graph
121 Commits
Author SHA1 Message Date
Ubuntu e55d8a3f1e newsletter: operator accounts are not stale subscribers
info@inference.coop is on the Members list but has no members row, so the
reconcile classified the co-op's own inbox as a stale subscriber and would have
unsubscribed it.

The data model already distinguishes this: OPERATOR_EMAILS (default
info@inference.coop,bot@inference.coop) are infrastructure accounts — the SSO
owner and the bot — which the panel surfaces under "admins" and excludes from
member counts. They live outside the members table by design. The Loomio
reconciliation applies the same rule to the same accounts via LOOMIO_ALWAYS_KEEP
("must always remain in the group").

So operators become the third class here: neither members nor stale. Their
subscription is left exactly as it is (never unsubscribed, and the reconcile
never subscribes, so an operator who unsubscribes stays unsubscribed). The
response reports how many were left, and the log line carries counts only.
2026-10-26 16:54:53 +00:00
Ubuntu 9495daa701 newsletter: the unsubscribe PUT needs the email field — it was a silent 400
The end-to-end probe against the live listmonk caught this: PUT
/api/subscribers/{id} with {"lists": []} returns 400 {"message":"Invalid
email."}, leaving the subscriber on the list. It needs {"email": ..., "lists":
[]}. The failed request was only a log warning, so the reconcile's main job —
taking lapsed members off the Members list — has never worked; only the DELETE
branch did anything, which is the branch this series just removed.

Now both classes unsubscribe successfully and keep their record.
2026-10-26 16:42:10 +00:00
Ubuntu 7bb676eb68 newsletter: unsubscribe stale subscribers instead of deleting them; sweep: apply the documented fail-safe
Two findings from the retrospective review of 394cccfb..e24c272e (#34).

listmonk_reconcile() deleted subscribers with no members row outright. That
destroyed the subscriber's consent/unsubscribe history, and a stray test account
is indistinguishable from a real person who subscribed outside the portal. Both
classes now leave the list the same way — PUT {"lists": []} — and the record is
kept as a suppression marker; nothing is purged automatically. The summary log
line reports counts by reason instead of subscriber addresses. Subscriber
pagination also gains a short-page fallback so a missing `total` cannot truncate
the reconcile at page 1.

reconcile_memberships() called fetch_members() unguarded, so the documented
fail-safe — skip reconciliation rather than deactivate everyone missing from a
partial list — was unreachable: since fetch_members() now raises, the sweep
returned a 500 instead of {"status": "skipped"}. It now catches the failure and
returns reason "member_fetch_failed". The two webhook call sites are left
unguarded on purpose: a payment webhook should fail loudly so OC retries it, and
it self-heals.

Refs #34
2026-10-26 16:38:52 +00:00
Ubuntu 492019088e loomio: only rename auto-generated handles, never one a human chose
Testing the previous commit against the live Loomio user list showed it would
also have renamed a member whose handle was chosen by hand (a working name
would have become the sanitized OIDC uid) — the uid is domain-shaped, so the
sanitizer produced a different, wrong target. The function is scoped to
Loomio's own auto-generated handles: the email local part with
non-alphanumerics stripped plus a random suffix. Gate on that shape.

Verified against the live user list (70 users) before building: exactly one
handle changes — the member whose username fix 422'd on every sweep — no
computed target violates Loomio's rule, and the hand-chosen handle is left
alone.
2026-10-26 16:19:42 +00:00
inference-bot 178a4aa360 loomio: sanitize usernames instead of falling back to another invalid one
The username fix rejected a dot-containing OAuth uid and then substituted the
email local part, which carries the same dots — so Loomio answered 422
("Usernames must contain lower case letters, numbers, underscores, and internal
hyphens only") on every sweep, for the one member whose Cloudron username has a
dot. Normalize instead: disallowed characters become underscores, and leading
and trailing hyphens/underscores are stripped. Also log the Loomio user id
rather than the member's email.
2026-10-26 16:07:23 +00:00
inference-bot e24c272e2e org-link fix: INSERT bound 4 values to 3 placeholders (500 on every link); slug = org slug 2026-10-21 21:16:59 -06:00
inference-bot d8d80d9594 newsletter reconcile: Members list mirrors ACTIVE members
Root cause of test accounts receiving the newsletter: LISTMONK_API_USER was
set EMPTY in the portal env - env-present beats the code default, so every
listmonk_sync authenticated with an empty user and 403'd silently. Fixed both
the env (set to portal-sync) and the code (empty env falls back to default).

New: listmonk_reconcile() - the Members list must mirror ACTIVE members.
Stale subscribers are removed: members marked inactive are taken off the list
(record kept as suppression marker); addresses that are not members at all
(test/deleted accounts) are deleted outright. Wired as step 4 of the nightly
membership sweep + POST /admin/newsletter/reconcile for on-demand runs.
2026-10-21 16:32:36 -06:00
inference-bot e4da4da2c6 pagination fix: OC MemberCollection has NO cursor pageInfo — use offset+totalCount pagination (verified against the live API); the cursor query 400'd, taking the sweep and org endpoints down 2026-10-20 23:30:32 -06:00
inference-bot 42c664c076 OC member queries: cursor pagination via fetch_all_member_nodes() — members(limit:100) silently hid everyone past member #100 (same class as the Cloudron 25-per-page trap). Helper RAISES on any page failure so callers' fail-safes apply (never a partial list — the sweep would deactivate everyone missing from one); fetch_members returns [] on failure (sweep skips), fetch_org_backers propagates (sweep keeps linked accounts active), webhook payer lookup and /admin/org-link wrapped. 2026-10-20 23:26:18 -06:00
inference-bot fed4a7005e audit fixes: two latent NameErrors in the org code — webhook org branch referenced undefined org_name (crashed on an unlinked org's first payment), org-links GET referenced undefined name (crashed when a linked org's dues show inactive) 2026-10-20 23:21:51 -06:00
inference-bot d87cbf9650 admin overview: expose matrix_mxid (dashboard-set external handle) per member 2026-10-20 23:01:24 -06:00
inference-bot dcffbb1b98 org-links GET: remaining name-column reference 2026-10-20 22:57:40 -06:00
inference-bot 394cccfbcb org links: members table has no name column — drop it from org SQL/unpackings (names come from OC/Cloudron) 2026-10-20 22:55:55 -06:00
inference-bot 72614f8f19 Organizational memberships: OC org contributors (type ORGANIZATION) never expose an email, so all three automated paths missed them (webhook email lookup, nightly sweep, /join). Add: members.org_slug link column + fetch_org_backers() (org BACKER/Membership nodes); sweep keeps linked accounts active while the org's dues are active and deactivates on lapse (fail-safe keeps active if the org fetch errors); webhook re-activates linked accounts on org dues and emails the operator for unlinked orgs; POST /admin/org-link (link + full provision incl. Cloudron signup link) and GET /admin/org-links (live dues status); admin overview gains an 'org' field. 2026-10-20 22:45:37 -06:00
inference-bot 5425efee1f overview: normalize float-residue spend (e.g. -5.55e-17 after cycle reset) to 0 so the panel never renders -0.00 2026-10-19 11:42:58 -06:00
inference-bot fd13bc6ea2 broker/matrix-mxid GET: local_mxid_for_email was async-def but never awaited — response contained a coroutine and FastAPI 500'd ('coroutine object is not iterable'), so the dashboard silently showed no registered Matrix ID. Make it a plain sync def (blocking urllib). 2026-10-19 10:47:49 -06:00
inference-bot 97ebe69553 portal: /admin/librechat/keys — dedicated-token feed of member chat keys for the sandbox's key auto-load 2026-10-18 23:15:38 -06:00
inference-bot 7f04920327 broker: retire the old LiteLLM key before re-creating a same-name key
LiteLLM requires unique key aliases, so re-creating a key with an existing
(email, name) pair 400s ('alias already exists') — breaking key rotation
from the dashboard. The stored LiteLLM hash lets us retire the old key
first; the ON CONFLICT upsert then tracks the new key.

Surfaced by the LibreChat sandbox work: the portal chat-key rotation for
an SSO member failed with exactly this 400.
2026-10-17 18:00:49 -06:00
Inferencebot 003689868f Remove PublicAI; Regolo becomes the Apertus provider; emoji = feature tier (🌱 green for greenpt+regolo), qwen-image via image feature only 2026-10-16 10:48:10 -06:00
Inferencebot ced82b09d0 portal: filter image-gen models from chat picker by name (gateway strips mode from /v1/models) 2026-10-16 10:26:04 -06:00
Inferencebot 5091c9e644 portal: add Regolo models to picker (Apertus 70B, Qwen 3.5 122B, Qwen Image); exclude image-gen from chat list 2026-10-16 10:24:27 -06:00
Inferencebot 106b563748 sync: invite members to MATRIX_EXTRA_ROOMS too (restricted rooms aren't federation-discoverable until joined) 2026-10-14 16:40:35 -06:00
Inferencebot 24f0c0f745 fix: import urllib.parse on the retire path (NameError broke the sweep) 2026-10-14 10:55:30 -06:00
Inferencebot b2689d9cd1 Matrix sync: revoke stale local-account invites too (directory hygiene + one-identity completeness) 2026-10-14 10:54:24 -06:00
Inferencebot 81b640ba74 matrix-mxid: return invite_sent so the dashboard can confirm the invite is on its way 2026-10-12 09:01:22 -06:00
Inferencebot e56d0456bb Chat models: silent auto-RAG via legacy FC (no knowledge tool, no narration) 2026-10-10 13:41:20 -06:00
Inferencebot 92e7ddec05 Matrix sync: retire local @username:inference.coop accounts when a member sets a dashboard MXID (one identity per member) 2026-10-10 13:29:26 -06:00
Inferencebot 05feed5f24 Strengthen docs-KB steering: firm negative instruction after soft hint proved insufficient 2026-10-09 22:44:00 -06:00
Inferencebot 2eab2abb0d Overview: separate admin/operator accounts into their own category (excluded from member counts) 2026-10-08 15:08:21 -06:00
Inferencebot a7a283d552 Steer docs-KB tool usage: params.system hint to query only co-op-relevant questions 2026-10-08 09:38:05 -06:00
Inferencebot 32420f60f4 Friendly names: tinfoil/glm-5.3 (replaces deprecated flash variant) 2026-10-07 22:07:44 -06:00
Inferencebot 1a227642d9 Attach co-op docs knowledge collection to every model (auto-RAG, no KB browsing wandering) 2026-10-07 10:52:08 -06:00
Inferencebot e18cd4b703 Provisioning: add new members to Open WebUI docs-RAG access group at signup 2026-10-07 10:38:32 -06:00
Inferencebot 2a5277007e Model list: also filter embedding models out of the chat picker 2026-10-07 09:44:15 -06:00
Inferencebot 3d258e9a2d Loomio sync: only invite activated members (avoid invite email before account setup) 2026-10-07 09:19:24 -06:00
inference-bot 38005127cb Expose credits_remaining (purchased minus cycle overspend) in broker usage + admin overview 2026-10-03 13:59:29 -06:00
inference-bot 052a109040 Fix credit model: allowance is a floor, credits add on top (max_budget = allowance + credits, not reduced by cycle overspend) 2026-10-03 13:06:02 -06:00
inference-bot 563881d63c Matrix: member-set extra MXID (matrix_mxid column + broker get/set + sync prefers it) 2026-10-03 10:48:13 -06:00
inference-bot 3cdc250d52 Chat model picker: value-tier emoji + friendly names in /v1/models (🔒 private / 🌱 green / 📖 public), id stays canonical 2026-10-02 22:30:11 -06:00
inference-bot 1fe0e47802 Update code-comment example (green-r -> glm-5.3-flash) 2026-10-02 22:04:54 -06:00
inference-bot 29823e87d5 Matrix mxid lookup: paginate Cloudron users API (25/page) — was missing page 2+ 2026-10-02 13:03:14 -06:00
inference-bot f86ead8653 Matrix invite: honor 429 retry_after_ms (3 attempts) 2026-10-02 12:29:41 -06:00
inference-bot 07a1f2ad4e Fix Cloudron users API response shape ({users: [...]}) 2026-10-02 12:28:39 -06:00
inference-bot 82ada8c95b Matrix: invite all active members to co-op Space + General room on every sync (SSO usernames via Cloudron lookup; MATRIX_EXTRA_MXIDS for federated accounts) 2026-10-02 12:27:45 -06:00
inference-bot cddd9b6e3c Loomio username fix: allow hyphens in uids (was falling through to email local-part for inference-admin/bot) 2026-10-02 08:41:03 -06:00
inference-bot ef253d01a0 Loomio: fix auto-generated usernames after sync (b3 server API; prefer Cloudron uid, fall back to email local-part) 2026-10-02 08:36:10 -06:00
inference-bot 070c6525cd Broker usage: expose all_time_spend (member's lifetime total from SpendLogs) in both DB and HTTP paths 2026-10-01 16:07:10 -06:00
inference-bot 7af02f6466 Model aggregation: order by requests (usage) not spend — matches leaderboard bars and member-facing intent 2026-10-01 15:56:53 -06:00
inference-bot 19c04f37fa Fix days-filter in aggregate_model_usage: COALESCE(startTime,'') is invalid SQL (empty-string not a timestamp) — windowed queries always failed to empty 2026-10-01 15:54:58 -06:00
inference-bot 69beb9c002 Broker: co-op-wide model leaderboard endpoint for member dashboard (aggregate, anonymized) 2026-10-01 15:35:57 -06:00