feat(adapters): run profile consolidation on the fast tier when one is mapped
Fact extraction is short structured work, so consolidation asks the model resolver for the fast tier. The resolver takes a per-call tier and, when the space has not mapped it, keeps the bot's own tier or model as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
parent
04b7513e57
commit
5fa5779b6b
@ -1798,6 +1798,40 @@ description: Prepare standup notes
|
|||||||
).resolves.toMatchObject({ provider: "xai", id: "grok-4.6" });
|
).resolves.toMatchObject({ provider: "xai", id: "grok-4.6" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prefers a tier the call asks for, and keeps the bot's choice when it is unmapped", async () => {
|
||||||
|
const fastTier = { ...routerTier, tier: "fast", modelId: "router-fast" };
|
||||||
|
const mapped = tierPrisma(
|
||||||
|
{ modelProvider: "xai", modelId: "grok-4.6", thinkingLevel: null, modelTier: null },
|
||||||
|
fastTier,
|
||||||
|
);
|
||||||
|
await expect(
|
||||||
|
mapped.executor.resolveModel({
|
||||||
|
userId: "user-1",
|
||||||
|
spaceId: "ws-1",
|
||||||
|
botId: "bot-1",
|
||||||
|
tier: "fast",
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({ provider: "openai-compatible", id: "router-fast" });
|
||||||
|
expect(mapped.findUnique).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
where: { spaceId_userId_tier: { spaceId: "ws-1", userId: "user-1", tier: "fast" } },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const unmapped = tierPrisma(
|
||||||
|
{ modelProvider: "xai", modelId: "grok-4.6", thinkingLevel: null, modelTier: null },
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await expect(
|
||||||
|
unmapped.executor.resolveModel({
|
||||||
|
userId: "user-1",
|
||||||
|
spaceId: "ws-1",
|
||||||
|
botId: "bot-1",
|
||||||
|
tier: "fast",
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({ provider: "xai", id: "grok-4.6" });
|
||||||
|
});
|
||||||
|
|
||||||
it("resolves a helper tier and reports an unmapped one as null", async () => {
|
it("resolves a helper tier and reports an unmapped one as null", async () => {
|
||||||
const mapped = tierPrisma(null, routerTier);
|
const mapped = tierPrisma(null, routerTier);
|
||||||
await expect(
|
await expect(
|
||||||
|
|||||||
@ -728,6 +728,8 @@ export function createRunExecutor(deps: ExecutorDeps) {
|
|||||||
userId: string;
|
userId: string;
|
||||||
spaceId: string;
|
spaceId: string;
|
||||||
botId?: string;
|
botId?: string;
|
||||||
|
/** Preferred tier for this call; when the space has not mapped it, the bot's choice holds. */
|
||||||
|
tier?: ModelTier;
|
||||||
}): Promise<AgentRunRequest["model"]> {
|
}): Promise<AgentRunRequest["model"]> {
|
||||||
const override = scope.botId
|
const override = scope.botId
|
||||||
? await deps.prisma.bot.findFirst({
|
? await deps.prisma.bot.findFirst({
|
||||||
@ -744,7 +746,16 @@ export function createRunExecutor(deps: ExecutorDeps) {
|
|||||||
hasOverride
|
hasOverride
|
||||||
? findModelCredential(deps.prisma, scope, override!.modelProvider!, override!.modelId)
|
? findModelCredential(deps.prisma, scope, override!.modelProvider!, override!.modelId)
|
||||||
: Promise.resolve(null),
|
: Promise.resolve(null),
|
||||||
override?.modelTier ? findSpaceModelTier(deps.prisma, scope, override.modelTier) : null,
|
(scope.tier
|
||||||
|
? findSpaceModelTier(deps.prisma, scope, scope.tier)
|
||||||
|
: Promise.resolve(null)
|
||||||
|
).then(
|
||||||
|
(preferred) =>
|
||||||
|
preferred ??
|
||||||
|
(override?.modelTier
|
||||||
|
? findSpaceModelTier(deps.prisma, scope, override.modelTier)
|
||||||
|
: null),
|
||||||
|
),
|
||||||
findDefaultModelCredential(deps.prisma, scope),
|
findDefaultModelCredential(deps.prisma, scope),
|
||||||
deps.prisma.deploymentSettings.findUnique({ where: { id: "default" } }),
|
deps.prisma.deploymentSettings.findUnique({ where: { id: "default" } }),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -336,6 +336,13 @@ describe("consolidateProfile", () => {
|
|||||||
expect(request.prompt).not.toContain("asthma");
|
expect(request.prompt).not.toContain("asthma");
|
||||||
expect(request.prompt.match(/<conversation>/g)).toHaveLength(2);
|
expect(request.prompt.match(/<conversation>/g)).toHaveLength(2);
|
||||||
|
|
||||||
|
// Extraction asks for the fast tier; an unmapped tier keeps the main bot's model.
|
||||||
|
expect(consolidateDeps.resolveModel).toHaveBeenCalledWith({
|
||||||
|
userId: "user-1",
|
||||||
|
spaceId: "space-1",
|
||||||
|
botId: "main-bot",
|
||||||
|
tier: "fast",
|
||||||
|
});
|
||||||
// Facts are written under the same per-user lock as every other profile writer.
|
// Facts are written under the same per-user lock as every other profile writer.
|
||||||
expect(prisma.$queryRaw).toHaveBeenCalledTimes(1);
|
expect(prisma.$queryRaw).toHaveBeenCalledTimes(1);
|
||||||
expect(prisma.userProfile.updateMany).toHaveBeenCalledWith({
|
expect(prisma.userProfile.updateMany).toHaveBeenCalledWith({
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type {
|
|||||||
JobPublisher,
|
JobPublisher,
|
||||||
} from "@rakazo/adapter-kit";
|
} from "@rakazo/adapter-kit";
|
||||||
import { profileConsolidateJob } from "@rakazo/adapter-kit";
|
import { profileConsolidateJob } from "@rakazo/adapter-kit";
|
||||||
import type { MessageBlock, UserFactCategory } from "@rakazo/contracts";
|
import type { MessageBlock, ModelTier, UserFactCategory } from "@rakazo/contracts";
|
||||||
import {
|
import {
|
||||||
SENSITIVE_USER_FACT_CATEGORIES,
|
SENSITIVE_USER_FACT_CATEGORIES,
|
||||||
USER_FACT_CATEGORIES,
|
USER_FACT_CATEGORIES,
|
||||||
@ -295,6 +295,7 @@ export interface ConsolidateProfileDeps {
|
|||||||
userId: string;
|
userId: string;
|
||||||
spaceId: string;
|
spaceId: string;
|
||||||
botId?: string;
|
botId?: string;
|
||||||
|
tier?: ModelTier;
|
||||||
}) => Promise<AgentRunRequest["model"]>;
|
}) => Promise<AgentRunRequest["model"]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +320,13 @@ export async function consolidateProfile(
|
|||||||
if (!main?.thread) return;
|
if (!main?.thread) return;
|
||||||
let model: AgentRunRequest["model"];
|
let model: AgentRunRequest["model"];
|
||||||
try {
|
try {
|
||||||
model = await deps.resolveModel({ userId, spaceId: main.spaceId, botId: main.id });
|
// Extraction is short structured work: the fast tier when the space maps one.
|
||||||
|
model = await deps.resolveModel({
|
||||||
|
userId,
|
||||||
|
spaceId: main.spaceId,
|
||||||
|
botId: main.id,
|
||||||
|
tier: "fast",
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
getLogger().info(`profile.consolidate skipped for user ${userId}: no usable model`, {
|
getLogger().info(`profile.consolidate skipped for user ${userId}: no usable model`, {
|
||||||
reason: error instanceof Error ? error.message : String(error),
|
reason: error instanceof Error ? error.message : String(error),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user