mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
Merge remote-tracking branch 'origin/release/v1.46.0' into fix/end-medication-contributor-base
# Conflicts: # src/__tests__/baselines/source-file-lines.json # src/__tests__/baselines/source-function-lines.json
This commit is contained in:
commit
a35ff5f5ea
47 files changed
+2606
-447
No files matched your search
@@ -67,6 +67,14 @@ vi.mock("@/lib/auth/audit", () => ({
|
||||
auditLog: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/ai/provider-health-ledger", () => ({
|
||||
forgetProviderHealth: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/insights/status-negative-window", () => ({
|
||||
clearStatusNegativeWindows: vi.fn(),
|
||||
}));
|
||||
|
||||
import { GET, PATCH } from "../route";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { resolveProviderAvailability } from "@/lib/ai/provider";
|
||||
@@ -77,6 +85,8 @@ import { getAssistantFlags } from "@/lib/feature-flags";
|
||||
import { providerCredentialPolicy } from "@/lib/sharing/provider-work-authority";
|
||||
import { hasActiveConsentForSurface } from "@/lib/ai/consent-guard";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { forgetProviderHealth } from "@/lib/ai/provider-health-ledger";
|
||||
import { clearStatusNegativeWindows } from "@/lib/insights/status-negative-window";
|
||||
|
||||
function patchRequest(body: unknown): Request {
|
||||
return new Request("http://localhost/api/user/ai-provider", {
|
||||
@@ -327,6 +337,26 @@ describe("PATCH /api/user/ai-provider — the gateway's own columns (#470)", ()
|
||||
);
|
||||
});
|
||||
|
||||
it("starts background work afresh after a change to what answers (#1279)", async () => {
|
||||
// A provider benched for background work and notes backing off would
|
||||
// otherwise sit out a pause the old setup earned.
|
||||
vi.mocked(isPublicUrl).mockReturnValue(true);
|
||||
const res = await patch(
|
||||
patchRequest({
|
||||
provider: "OPENAI_COMPATIBLE",
|
||||
compatModel: "gpt-oss-120b",
|
||||
}),
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
expect(vi.mocked(forgetProviderHealth).mock.calls.map((c) => c[0])).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ userId: "u-1", providerType: "openai-compatible" },
|
||||
{ userId: "u-1", providerType: "local" },
|
||||
]),
|
||||
);
|
||||
expect(clearStatusNegativeWindows).toHaveBeenCalledWith("u-1");
|
||||
});
|
||||
|
||||
it("refuses a private base URL the operator has not allowlisted", async () => {
|
||||
vi.mocked(isPublicUrl).mockReturnValue(false);
|
||||
vi.mocked(isLocalAiHostAllowed).mockReturnValue(false);
|
||||
|
||||
@@ -23,9 +23,39 @@ import {
|
||||
serializeProviderChain,
|
||||
} from "@/lib/ai/provider-chain";
|
||||
import { reasoningEffortFor } from "@/lib/ai/reasoning-effort";
|
||||
import { forgetProviderHealth } from "@/lib/ai/provider-health-ledger";
|
||||
import type { ProviderChainType } from "@/lib/ai/provider-chain";
|
||||
import { clearStatusNegativeWindows } from "@/lib/insights/status-negative-window";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** The chain entries this route configures (Codex signs in elsewhere). */
|
||||
const USER_SETTINGS_PROVIDERS: readonly ProviderChainType[] = [
|
||||
"openai",
|
||||
"anthropic",
|
||||
"local",
|
||||
"openai-compatible",
|
||||
];
|
||||
|
||||
/**
|
||||
* Write the changed provider settings. #1279 — and start background work
|
||||
* afresh: it skips a provider the health ledger benched, and a status note
|
||||
* waits out its negative window, so without this a fixed setup would sit out
|
||||
* a pause the old one earned.
|
||||
*/
|
||||
async function saveProviderSettings(
|
||||
userId: string,
|
||||
updates: Record<string, unknown>,
|
||||
): Promise<void> {
|
||||
await prisma.user.update({ where: { id: userId }, data: updates });
|
||||
await Promise.all([
|
||||
...USER_SETTINGS_PROVIDERS.map((providerType) =>
|
||||
forgetProviderHealth({ userId, providerType }),
|
||||
),
|
||||
clearStatusNegativeWindows(userId),
|
||||
]);
|
||||
}
|
||||
|
||||
export const GET = apiHandler(async () => {
|
||||
const { user } = await requireAuth();
|
||||
annotate({ action: { name: "user.ai-provider.get" } });
|
||||
@@ -389,7 +419,7 @@ export const PATCH = apiHandler(async (request: NextRequest) => {
|
||||
: null;
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await prisma.user.update({ where: { id: user.id }, data: updates });
|
||||
await saveProviderSettings(user.id, updates);
|
||||
}
|
||||
|
||||
for (const [slot, value, column] of keySlots) {
|
||||
|
||||
Reference in new issue
Block a user