refactor(insights): share workbook export and ai defaults

This commit is contained in:
2026-03-31 22:53:53 +02:00
parent 05eeaab3f7
commit 160ba99b5c
8 changed files with 272 additions and 61 deletions
@@ -1,4 +1,9 @@
import { DEFAULT_OPENAI_MODEL } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import type {
ChatCompletion,
ChatCompletionCreateParamsNonStreaming,
} from "openai/resources/chat/completions/completions";
import { z } from "zod";
import { createAiClient, isAiConfigured, loggedAiCall, parseAiError } from "../ai-client.js";
import type { TRPCContext } from "../trpc.js";
@@ -73,13 +78,15 @@ export async function generateProjectNarrative(
throw new TRPCError({ code: "NOT_FOUND", message: "Project not found" });
}
if (!isAiConfigured(settings)) {
if (!settings || !isAiConfigured(settings)) {
throw new TRPCError({
code: "PRECONDITION_FAILED",
message: "AI is not configured. Please set credentials in Admin → Settings.",
});
}
const configuredSettings = settings;
const now = new Date();
const totalDays = countBusinessDays(project.startDate, project.endDate);
const elapsedDays = countBusinessDays(
@@ -125,27 +132,30 @@ export async function generateProjectNarrative(
${dataContext}`;
const client = createAiClient(settings);
const model = settings.azureOpenAiDeployment;
const maxTokens = settings.aiMaxCompletionTokens ?? 300;
const temperature = settings.aiTemperature ?? 1;
const provider = settings.aiProvider ?? "openai";
const client = createAiClient(configuredSettings);
const model = configuredSettings.azureOpenAiDeployment ?? DEFAULT_OPENAI_MODEL;
const maxTokens = configuredSettings.aiMaxCompletionTokens ?? 300;
const temperature = configuredSettings.aiTemperature ?? 1;
const provider = configuredSettings.aiProvider ?? "openai";
let narrative = "";
try {
const completion = await loggedAiCall(provider, model, prompt.length, () =>
client.chat.completions.create({
messages: [
{
role: "system",
content: "You are a project management analyst providing brief executive summaries. Be factual and action-oriented.",
},
{ role: "user", content: prompt },
],
max_completion_tokens: maxTokens,
model,
...(temperature !== 1 ? { temperature } : {}),
}),
const completionRequest: ChatCompletionCreateParamsNonStreaming = {
messages: [
{
role: "system",
content: "You are a project management analyst providing brief executive summaries. Be factual and action-oriented.",
},
{ role: "user", content: prompt },
],
max_completion_tokens: maxTokens,
model,
stream: false,
...(temperature !== 1 ? { temperature } : {}),
};
const completion = await loggedAiCall<ChatCompletion>(provider, model, prompt.length, () =>
client.chat.completions.create(completionRequest),
);
narrative = completion.choices[0]?.message?.content?.trim() ?? "";
} catch (error) {