add analysis recommendation

This commit is contained in:
Danel Kungla
2025-09-17 17:35:04 +03:00
parent b96ef47b2d
commit 1b634f9736
6 changed files with 1100 additions and 799 deletions

View File

@@ -0,0 +1,153 @@
import { cache } from 'react';
import { unstable_cache } from 'next/cache';
import { AccountWithParams } from '@/packages/features/accounts/src/types/accounts';
import crypto from 'crypto';
import OpenAI from 'openai';
import PersonalCode from '~/lib/utils';
import { AnalysisTestResponse } from '../../(dashboard)/analysis-results/test/test-responses';
import { OrderAnalysisCard } from '../../_components/order-analyses-cards';
export const loadRecommendations = cache(recommendationsLoader);
type FormattedAnalysisResponse = {
value: string;
name: string;
responseTime: string;
};
function canonical(v: unknown): string {
return JSON.stringify(v, (_k, val) => {
if (val && typeof val === 'object' && !Array.isArray(val)) {
// sort object keys for stable JSON
return Object.keys(val)
.sort()
.reduce((o: any, k) => ((o[k] = (val as any)[k]), o), {});
}
return val;
});
}
const sha256 = (s: string) =>
crypto.createHash('sha256').update(s).digest('hex');
const getLatestResponseTime = (items: FormattedAnalysisResponse[]) => {
if (!items?.length) return null;
let latest = null;
for (const it of items) {
const d = new Date(it.responseTime);
const t = d.getTime();
if (!Number.isNaN(t) && (latest === null || t > latest.getTime())) {
latest = d;
}
}
return latest;
};
const getLatestUniqueAnalysResponses = (
analysisResponses: AnalysisTestResponse[],
): { name: string; value: string; responseTime: string }[] => {
const analysisElements = analysisResponses
.map(({ orderedAnalysisElements }) => orderedAnalysisElements)
.flat();
console.log('analysisElements', analysisElements.length);
const map = new Map();
for (const it of analysisElements) {
const prev = map.get(it.analysisName);
if (it.results.responseTime) {
if (
!prev ||
new Date(it.results.responseTime) > new Date(prev.responseTime)
) {
map.set(it.analysisName, {
name: it.analysisName,
value: it.results.responseValue,
responseTime: it.results.responseTime,
});
}
}
}
return [...map.values()];
};
async function recommendationsLoader(
analysisResponses: AnalysisTestResponse[],
analyses: OrderAnalysisCard[],
account: AccountWithParams | null,
): Promise<any> {
if (!account?.personal_code) {
return [];
}
const client = new OpenAI();
const { gender } = PersonalCode.parsePersonalCode(account.personal_code);
console.log('analysisResponses', analysisResponses);
console.log('analyises', analyses);
const latestUniqueAnalysResponses =
getLatestUniqueAnalysResponses(analysisResponses);
const latestResponseTime = getLatestResponseTime(latestUniqueAnalysResponses);
const latestISO = latestResponseTime
? new Date(latestResponseTime).toISOString()
: 'none';
console.log('latestResponseTime', latestResponseTime);
const formattedAnalysisResponses = latestUniqueAnalysResponses.map(
({ name, value }) => ({ name, value }),
);
const formattedAnalyses = analyses.map(({ description, title }) => ({
description,
title,
}));
console.log('formattedAnalyses', JSON.stringify(formattedAnalyses));
console.log(
'latestUniqueAnalysResponses',
JSON.stringify(latestUniqueAnalysResponses),
);
const response = await client.responses.create({
model: 'gpt-5',
store: false,
prompt: {
id: 'pmpt_68ca9c8bfa8c8193b27eadc6496c36440df449ece4f5a8dd',
variables: {
analyses: JSON.stringify(formattedAnalyses),
results: JSON.stringify(formattedAnalysisResponses),
gender: gender.value,
},
},
});
const responseJson = JSON.parse(response.output_text);
console.log('responseJson: ', responseJson);
const keyPayload = {
model: 'gpt-5', // swap to a model your project can access
promptId: 'pmpt_68ca9c8bfa8c8193b27eadc6496c36440df449ece4f5a8dd',
latestISO,
};
const key = 'recs:' + sha256(canonical(keyPayload));
const run = unstable_cache(async () => {
const response = await client.responses.create({
model: keyPayload.model,
store: false,
prompt: {
id: keyPayload.promptId,
variables: {
analyses: JSON.stringify(formattedAnalyses),
results: JSON.stringify(latestUniqueAnalysResponses),
gender: gender.value,
},
},
});
const json = JSON.parse(response.output_text);
return json.recommended;
}, ['recommendations', key]);
return await run();
}