Skip to content

Commit 3f61c63

Browse files
committed
Merge remote-tracking branch 'origin/master' into feat/1201/ruff
2 parents 9af8cb5 + f5b11ce commit 3f61c63

11 files changed

Lines changed: 96 additions & 52 deletions

File tree

app/next-client-app/api/recommendations.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,41 @@
22

33
import request from "@/lib/api/request";
44
import {
5-
unisonBaseUrl,
6-
unisonApiKey,
7-
recommendationService,
5+
recommendationServiceBaseUrl,
6+
recommendationServiceApiKey,
7+
recommendationServiceName,
88
} from "@/constants";
99

10-
export const getConceptRecommendationsUnison = async (
11-
// Unison can query by concept name, or concept code (exact match).
12-
// The latter is used first in searching, then the former.
10+
export const getRecommendations = async (
1311
queryValue: string,
1412
domainId: string
15-
): Promise<UnisonConceptResponse> => {
13+
): Promise<RecommendationServiceResponse> => {
1614
try {
17-
if (recommendationService === "unison") {
18-
const endpoint = `${queryValue}?apiKey=${unisonApiKey}&domain=${domainId}`;
19-
return await request<UnisonConceptResponse>(endpoint, {
20-
baseUrl: unisonBaseUrl,
15+
// Unison recommendation service
16+
// Unison can query by concept name, or concept code (exact match).
17+
// The latter is used first in searching, then the former.
18+
if (recommendationServiceName === "unison") {
19+
const endpoint = `${queryValue}?apiKey=${recommendationServiceApiKey}&domain=${domainId}`;
20+
return await request<RecommendationServiceResponse>(endpoint, {
21+
baseUrl: recommendationServiceBaseUrl,
2122
headers: {
2223
Accept: "application/json",
2324
},
2425
});
2526
}
26-
// TODO: Implement Lettuce recommendation service
27-
else if (recommendationService === "lettuce") {
28-
console.log("Lettuce recommendation service");
27+
// Lettuce recommendation service
28+
if (recommendationServiceName === "lettuce") {
29+
const endpoint = `${queryValue}?domain=${domainId}`;
30+
return await request<RecommendationServiceResponse>(endpoint, {
31+
baseUrl: recommendationServiceBaseUrl,
32+
headers: {
33+
Accept: "application/json",
34+
Authorization: `Bearer ${recommendationServiceApiKey}`,
35+
},
36+
authMode: "apiKey",
37+
});
2938
}
30-
39+
// Other recommendation services not supported
3140
throw new Error("Recommendation service not supported");
3241
} catch (error) {
3342
console.error("Error fetching recommendations:", error);

app/next-client-app/components/core/Tooltips.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
Tooltip,
33
TooltipContent,
44
TooltipProvider,
5-
TooltipTrigger
5+
TooltipTrigger,
66
} from "@/components/ui/tooltip";
77

88
import { InfoIcon } from "lucide-react";
@@ -22,7 +22,10 @@ export function Tooltips({
2222
<TooltipTrigger asChild>
2323
<InfoIcon className="ml-1 h-4 w-4 text-muted-foreground" />
2424
</TooltipTrigger>
25-
<TooltipContent className="max-w-96 text-center" side={side}>
25+
<TooltipContent
26+
className="max-w-96 text-center whitespace-pre-wrap"
27+
side={side}
28+
>
2629
<p>
2730
{content}
2831
{link && (

app/next-client-app/components/recommendations/ai-suggesions-button.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { useState } from "react";
33
import { Button } from "../ui/button";
44
import { Sparkles, Loader2 } from "lucide-react";
55
import AISuggestionDialog from "./ai-suggestions-dialog";
6-
import { getConceptRecommendationsUnison } from "@/api/recommendations";
6+
import { getRecommendations } from "@/api/recommendations";
77
import { addConcept } from "@/api/concepts";
88
import { toast } from "sonner";
99
import {
1010
DropdownMenu,
1111
DropdownMenuContent,
1212
DropdownMenuLabel,
1313
DropdownMenuSeparator,
14-
DropdownMenuTrigger
14+
DropdownMenuTrigger,
1515
} from "@/components/ui/dropdown-menu";
1616
import { domains } from "@/constants/domains";
1717
import { DropdownMenuItem } from "@radix-ui/react-dropdown-menu";
@@ -20,7 +20,7 @@ export function AISuggestionsButton({
2020
value,
2121
tableId,
2222
rowId,
23-
contentType
23+
contentType,
2424
}: {
2525
value: string;
2626
tableId: string;
@@ -29,7 +29,8 @@ export function AISuggestionsButton({
2929
}) {
3030
const [isLoading, setIsLoading] = useState(false);
3131
const [isOpen, setIsOpen] = useState(false);
32-
const [suggestions, setSuggestions] = useState<UnisonConceptItem[]>([]);
32+
const [suggestions, setSuggestions] = useState<RecommendationItem[]>([]);
33+
const [metadata, setMetadata] = useState<RecommendationMetadata | null>(null);
3334
const [domainId, setDomainId] = useState<string>("");
3435

3536
// Fetches AI suggestions from the API
@@ -42,14 +43,16 @@ export function AISuggestionsButton({
4243
setIsLoading(true);
4344

4445
try {
45-
// Call the getConceptRecommendations function
46-
const recommendations: UnisonConceptResponse =
47-
await getConceptRecommendationsUnison(value, domainId);
46+
const recommendations: RecommendationServiceResponse =
47+
await getRecommendations(value, domainId);
4848
// Filter to get only unique concept IDs
4949
const uniqueRecommendations = recommendations.items.filter(
5050
(item, index, array) =>
5151
array.findIndex((i) => i.conceptId === item.conceptId) === index
5252
);
53+
if (recommendations.metadata) {
54+
setMetadata(recommendations.metadata);
55+
}
5356

5457
setSuggestions(uniqueRecommendations);
5558
setIsOpen(true);
@@ -136,6 +139,7 @@ export function AISuggestionsButton({
136139
rowId={rowId}
137140
domainId={domainId}
138141
contentType={contentType}
142+
metadata={metadata}
139143
/>
140144
</>
141145
);

app/next-client-app/components/recommendations/ai-suggestions-dialog.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { InfoItem } from "../core/InfoItem";
1010
interface AISuggestionDialogProps {
1111
open: boolean;
1212
onOpenChange: (open: boolean) => void;
13-
suggestions: UnisonConceptItem[];
13+
suggestions: RecommendationItem[];
1414
onApplySuggestion: (data: {
1515
concept: number;
1616
object_id: number;
@@ -23,6 +23,7 @@ interface AISuggestionDialogProps {
2323
rowId: number;
2424
domainId: string;
2525
contentType: string;
26+
metadata?: RecommendationMetadata | null;
2627
}
2728

2829
// Main dialog component combining all parts
@@ -36,6 +37,7 @@ export default function AISuggestionDialog({
3637
rowId,
3738
domainId,
3839
contentType,
40+
metadata,
3941
}: AISuggestionDialogProps) {
4042
return (
4143
<Dialog open={open} onOpenChange={onOpenChange}>
@@ -66,6 +68,16 @@ export default function AISuggestionDialog({
6668
}
6769
className="py-1 md:py-0 md:px-3"
6870
/>
71+
{metadata && (
72+
<InfoItem
73+
label="Pipeline"
74+
value={
75+
metadata.pipeline.charAt(0).toUpperCase() +
76+
metadata.pipeline.slice(1)
77+
}
78+
className="py-1 md:py-0 md:px-3"
79+
/>
80+
)}
6981
</div>
7082
</DialogHeader>
7183

app/next-client-app/components/recommendations/columns.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ export const columns = (
1616
table_id: string;
1717
}) => void,
1818
contentType: string
19-
): ColumnDef<UnisonConceptItem>[] => [
19+
): ColumnDef<RecommendationItem>[] => [
2020
{
2121
id: "Concept Name",
2222
header: ({ column }) => (
2323
<DataTableColumnHeader column={column} title="Concept Name" />
2424
),
2525
cell: ({ row }) => {
2626
const { conceptName } = row.original;
27-
return <div className="w-[500px]">{conceptName}</div>;
27+
return <div className="w-[500px] whitespace-pre-wrap">{conceptName}</div>;
2828
},
2929
enableSorting: false,
3030
enableHiding: true,
@@ -63,13 +63,20 @@ export const columns = (
6363
id: "Accuracy",
6464
header: ({ column }) => (
6565
<div className="text-center w-full">
66-
<DataTableColumnHeader column={column} title="Accuracy/Confidence" />
66+
<DataTableColumnHeader column={column} title="Accuracy/Score" />
6767
</div>
6868
),
6969
enableSorting: false,
7070
enableHiding: true,
7171
cell: ({ row }) => {
72-
const { accuracy, explanation } = row.original;
72+
const { accuracy, explanation, scores } = row.original;
73+
if (scores) {
74+
return (
75+
<div className="text-center w-full flex items-center justify-center gap-1">
76+
{((1 - (scores?.["vector-search"] || 0)) * 100).toFixed(2)}%
77+
</div>
78+
);
79+
}
7380
return (
7481
<div className="text-center w-full flex items-center justify-center gap-1">
7582
{accuracy}

app/next-client-app/components/recommendations/stored-recommendations-button.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
DropdownMenuContent,
1111
DropdownMenuLabel,
1212
DropdownMenuSeparator,
13-
DropdownMenuTrigger
13+
DropdownMenuTrigger,
1414
} from "@/components/ui/dropdown-menu";
1515
import { domains } from "@/constants/domains";
1616
import { DropdownMenuItem } from "@radix-ui/react-dropdown-menu";
@@ -20,7 +20,7 @@ export function StoredRecommendationsButton({
2020
tableId,
2121
rowId,
2222
contentType,
23-
mappingRecommendations
23+
mappingRecommendations,
2424
}: {
2525
value: string;
2626
tableId: string;
@@ -31,7 +31,7 @@ export function StoredRecommendationsButton({
3131
mappingRecommendations: MappingRecommendation[];
3232
}) {
3333
const [isOpen, setIsOpen] = useState(false);
34-
const [suggestions, setSuggestions] = useState<UnisonConceptItem[]>([]);
34+
const [suggestions, setSuggestions] = useState<RecommendationItem[]>([]);
3535
const [domainId, setDomainId] = useState<string>("");
3636

3737
// Handle click to show stored recommendations
@@ -57,7 +57,7 @@ export function StoredRecommendationsButton({
5757
// Transform mapping recommendations to expected format
5858
const transformMappingRecommendations = (
5959
recommendations: MappingRecommendation[]
60-
): UnisonConceptItem[] => {
60+
): RecommendationItem[] => {
6161
return recommendations.map((rec) => ({
6262
accuracy: rec.score ?? null,
6363
conceptId: rec.concept.concept_id,
@@ -68,7 +68,7 @@ export function StoredRecommendationsButton({
6868
conceptClass: rec.concept.concept_class_id ?? "Unknown",
6969
explanation: rec.score
7070
? `Pre-computed recommendation from ${rec.tool_name} (score: ${rec.score})`
71-
: `Pre-computed recommendation from ${rec.tool_name} (no score)`
71+
: `Pre-computed recommendation from ${rec.tool_name} (no score)`,
7272
}));
7373
};
7474

app/next-client-app/components/recommendations/stored-recommendations-dialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { InfoItem } from "../core/InfoItem";
99
interface RecommendationsDialogProps {
1010
open: boolean;
1111
onOpenChange: (open: boolean) => void;
12-
suggestions: UnisonConceptItem[];
12+
suggestions: RecommendationItem[];
1313
onApplySuggestion: (data: {
1414
concept: number;
1515
object_id: number;
@@ -36,7 +36,7 @@ export default function RecommendationsDialog({
3636
rowId,
3737
domainId,
3838
contentType,
39-
source
39+
source,
4040
}: RecommendationsDialogProps) {
4141
const getSourceLabel = () => {
4242
return source === "v3"

app/next-client-app/constants/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { MAX_FILE_SIZE_BYTES } from "./config";
33

44
export const apiUrl = process.env.BACKEND_URL;
55

6-
export const recommendationService = process.env.RECOMMENDATION_SERVICE;
6+
export const recommendationServiceBaseUrl =
7+
process.env.RECOMMENDATION_SERVICE_BASE_URL;
78

8-
export const unisonBaseUrl = process.env.UNISON_BASE_URL;
9-
10-
export const unisonApiKey = process.env.UNISON_API_KEY;
9+
export const recommendationServiceApiKey =
10+
process.env.RECOMMENDATION_SERVICE_API_KEY;
1111

1212
export const enableReuseTriggerOption = env(
1313
"NEXT_PUBLIC_ENABLE_REUSE_TRIGGER_OPTION"
@@ -22,7 +22,7 @@ export const enableStoredRecommendation = env(
2222
);
2323

2424
export const recommendationServiceName = env(
25-
"NEXT_PUBLIC_RECOMMENDATION_SERVICE_NAME"
25+
"NEXT_PUBLIC_RECOMMENDATION_SERVICE"
2626
);
2727

2828
// Re-export MAX_FILE_SIZE_BYTES from config.js

app/next-client-app/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
interface UnisonConceptItem {
1+
interface RecommendationItem {
22
accuracy: number | null;
33
conceptId: number;
44
conceptName: string;
@@ -7,11 +7,21 @@ interface UnisonConceptItem {
77
domain: string;
88
conceptClass: string;
99
explanation: string;
10+
standardConcept?: string;
11+
scores?: { "vector-search": number };
1012
}
1113

12-
interface UnisonConceptResponse {
13-
items: UnisonConceptItem[];
14-
count: number;
14+
interface RecommendationMetadata {
15+
assistant: string;
16+
version: string;
17+
pipeline: string;
18+
info: string | null;
19+
}
20+
21+
interface RecommendationServiceResponse {
22+
items: RecommendationItem[];
23+
count?: number;
24+
metadata?: RecommendationMetadata;
1525
}
1626

1727
interface MappingRecommendation {
@@ -23,4 +33,4 @@ interface MappingRecommendation {
2333
tool_name: string;
2434
tool_version: string;
2535
created_at: Date;
26-
}
36+
}

0 commit comments

Comments
 (0)