feat(FN-742): add describeModel helper and log model in agent creation sites
- Add describeModel() helper in pi.ts to format provider/model info for logging - Log resolved model details in executor, reviewer, and triage agent creation - Update executor and reviewer to call describeModel before session start - Add unit tests for describeModel covering all input combinations - Fix test mocks to account for new describeModel dependency
This commit is contained in:
@@ -4,6 +4,7 @@ import { AgentSemaphore } from "./concurrency.js";
|
||||
// Mock external dependencies
|
||||
vi.mock("./pi.js", () => ({
|
||||
createKbAgent: vi.fn(),
|
||||
describeModel: vi.fn().mockReturnValue("mock-provider/mock-model"),
|
||||
}));
|
||||
vi.mock("./reviewer.js", () => ({
|
||||
reviewStep: vi.fn(),
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TaskStore, Task, TaskDetail, StepStatus, Settings, WorkflowStep, M
|
||||
import { findWorktreeUser } from "./merger.js";
|
||||
import { generateWorktreeName, slugify } from "./worktree-names.js";
|
||||
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||
import { createKbAgent } from "./pi.js";
|
||||
import { createKbAgent, describeModel } from "./pi.js";
|
||||
import { reviewStep, type ReviewVerdict } from "./reviewer.js";
|
||||
import type { ToolDefinition, AgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
import { PRIORITY_EXECUTE, type AgentSemaphore } from "./concurrency.js";
|
||||
@@ -593,6 +593,9 @@ export class TaskExecutor {
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
});
|
||||
|
||||
executorLog.log(`${task.id}: using model ${describeModel(session)}`);
|
||||
await this.store.logEntry(task.id, `Executor using model: ${describeModel(session)}`);
|
||||
|
||||
// Make session available to custom tools (task_update checkpoint capture, review_step rewind)
|
||||
sessionRef.current = session;
|
||||
|
||||
@@ -1369,6 +1372,9 @@ If issues are found that need attention, describe them clearly.`;
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
});
|
||||
|
||||
executorLog.log(`${task.id}: workflow step '${workflowStep.name}' using model ${describeModel(session)}`);
|
||||
await this.store.logEntry(task.id, `Workflow step '${workflowStep.name}' using model: ${describeModel(session)}`);
|
||||
|
||||
let output = "";
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "message_update") {
|
||||
|
||||
37
packages/engine/src/pi.test.ts
Normal file
37
packages/engine/src/pi.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describeModel } from "./pi.js";
|
||||
import type { AgentSession } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
describe("describeModel", () => {
|
||||
it('returns "provider/modelId" when session has a model', () => {
|
||||
const fakeSession = {
|
||||
model: {
|
||||
provider: "anthropic",
|
||||
id: "claude-sonnet-4-5",
|
||||
name: "Claude Sonnet",
|
||||
},
|
||||
} as unknown as AgentSession;
|
||||
|
||||
expect(describeModel(fakeSession)).toBe("anthropic/claude-sonnet-4-5");
|
||||
});
|
||||
|
||||
it('returns "unknown model" when session model is undefined', () => {
|
||||
const fakeSession = {
|
||||
model: undefined,
|
||||
} as unknown as AgentSession;
|
||||
|
||||
expect(describeModel(fakeSession)).toBe("unknown model");
|
||||
});
|
||||
|
||||
it("handles different providers", () => {
|
||||
const fakeSession = {
|
||||
model: {
|
||||
provider: "openai",
|
||||
id: "gpt-4o",
|
||||
name: "GPT-4o",
|
||||
},
|
||||
} as unknown as AgentSession;
|
||||
|
||||
expect(describeModel(fakeSession)).toBe("openai/gpt-4o");
|
||||
});
|
||||
});
|
||||
@@ -22,6 +22,17 @@ export interface AgentResult {
|
||||
session: AgentSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a human-readable model description from an AgentSession.
|
||||
* Returns `"<provider>/<modelId>"` (e.g. `"anthropic/claude-sonnet-4-5"`)
|
||||
* or `"unknown model"` when the session has no model set.
|
||||
*/
|
||||
export function describeModel(session: AgentSession): string {
|
||||
const model = session.model;
|
||||
if (!model) return "unknown model";
|
||||
return `${model.provider}/${model.id}`;
|
||||
}
|
||||
|
||||
export interface AgentOptions {
|
||||
cwd: string;
|
||||
systemPrompt: string;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { AgentSemaphore } from "./concurrency.js";
|
||||
|
||||
vi.mock("./pi.js", () => ({
|
||||
createKbAgent: vi.fn(),
|
||||
describeModel: vi.fn().mockReturnValue("mock-provider/mock-model"),
|
||||
}));
|
||||
vi.mock("./reviewer.js", () => ({
|
||||
reviewStep: vi.fn(),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
vi.mock("./pi.js", () => ({
|
||||
createKbAgent: vi.fn(),
|
||||
describeModel: vi.fn().mockReturnValue("mock-provider/mock-model"),
|
||||
}));
|
||||
|
||||
import { reviewStep } from "./reviewer.js";
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
*/
|
||||
|
||||
import type { TaskStore } from "@fusion/core";
|
||||
import { createKbAgent } from "./pi.js";
|
||||
import { createKbAgent, describeModel } from "./pi.js";
|
||||
import { AgentLogger } from "./agent-logger.js";
|
||||
import { reviewerLog } from "./logger.js";
|
||||
import { checkSessionError } from "./usage-limit-detector.js";
|
||||
|
||||
const REVIEWER_SYSTEM_PROMPT = `You are an independent code and plan reviewer.
|
||||
@@ -209,6 +210,11 @@ export async function reviewStep(
|
||||
defaultThinkingLevel: options.defaultThinkingLevel,
|
||||
});
|
||||
|
||||
reviewerLog.log(`${taskId}: reviewer using model ${describeModel(session)}`);
|
||||
if (options.store && options.taskId) {
|
||||
await options.store.logEntry(options.taskId, `Reviewer using model: ${describeModel(session)}`);
|
||||
}
|
||||
|
||||
let reviewText = "";
|
||||
|
||||
// Capture the reviewer's full text output (still needed for verdict extraction)
|
||||
|
||||
@@ -11,7 +11,7 @@ import type {
|
||||
ToolDefinition,
|
||||
AgentSession,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { createKbAgent } from "./pi.js";
|
||||
import { createKbAgent, describeModel } from "./pi.js";
|
||||
import { reviewStep, type ReviewVerdict } from "./reviewer.js";
|
||||
import { PRIORITY_SPECIFY, type AgentSemaphore } from "./concurrency.js";
|
||||
import { AgentLogger } from "./agent-logger.js";
|
||||
@@ -477,6 +477,9 @@ export class TriageProcessor {
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
});
|
||||
|
||||
triageLog.log(`${task.id}: using model ${describeModel(session)}`);
|
||||
await this.store.logEntry(task.id, `Triage using model: ${describeModel(session)}`);
|
||||
|
||||
// Make session available to review_spec tool (for RETHINK rewind)
|
||||
sessionRef.current = session;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user