Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dylan/s3en 2393 update UI tracing tab to show the prompt version being used v2 #154

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
adding prompt combobox, api route, service function
  • Loading branch information
dylanzuber-scale3 committed Jun 25, 2024
commit b90755f23ece0b37eb4a43d5303efdc289133680
37 changes: 37 additions & 0 deletions app/api/prompt-ids/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { authOptions } from "@/lib/auth/options";
import { TraceService } from "@/lib/services/trace_service";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { NextRequest, NextResponse } from "next/server";

export async function GET(req: NextRequest) {
const session = await getServerSession(authOptions);

if (!session || !session.user) {
redirect("/login");
}

try {
const projectId = req.nextUrl.searchParams.get("projectId") as string;

if (!projectId) {
return NextResponse.json(
{ error: "Please provide a projectId" },
{ status: 400 }
);
}

const traceService = new TraceService();
const promptIDs = await traceService.GetPromptsInProject(projectId);
return NextResponse.json(
{ promptIDs },
{
status: 200,
}
);
} catch (error) {
return NextResponse.json(JSON.stringify({ error }), {
status: 400,
});
}
}
22 changes: 21 additions & 1 deletion components/project/traces/trace-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ClearIcon from "@mui/icons-material/Clear";
import { Check, ChevronsUpDown } from "lucide-react";
import { useEffect, useState } from "react";

import { PromptCombobox } from "@/components/shared/prompt-combobox";
import { UserCombobox } from "@/components/shared/user-combobox";
import { SpanAttributes } from "@/lib/ts_sdk_constants";
import VendorDropdown from "./vendor-dropdown";
Expand All @@ -43,6 +44,7 @@ export default function FilterDialog({
const [selectedFilters, setSelectedFilters] = useState<string[]>([]);
const [advancedFilters, setAdvancedFilters] = useState<any[]>([]);
const [selectedUserId, setSelectedUserId] = useState<string>("");
const [selectedPromptId, setSelectedPromptId] = useState<string>("");

useEffect(() => {
if (!open) {
Expand Down Expand Up @@ -97,6 +99,15 @@ export default function FilterDialog({
});
}

if (selectedPromptId) {
convertedAdvancedFilters.push({
key: "prompt_id",
operation: "EQUALS",
value: selectedPromptId,
type: "attribute",
});
}

onApplyFilters({
filters: [...convertedFilters, ...convertedAdvancedFilters],
});
Expand Down Expand Up @@ -181,19 +192,28 @@ export default function FilterDialog({
setSelectedUser={setSelectedUserId}
/>
</div>
<div>
<h4 className="mt-4">Prompt Id</h4>
<PromptCombobox
selectedPrompt={selectedPromptId}
setSelectedPrompt={setSelectedPromptId}
/>
</div>
<DialogFooter>
<Button variant={"outline"} onClick={onClose}>
Cancel
</Button>
{(selectedFilters.length > 0 ||
advancedFilters.length > 0 ||
selectedUserId !== "") && (
selectedUserId !== "" ||
selectedPromptId !== "") && (
<Button
variant={"destructive"}
onClick={() => {
setSelectedFilters([]);
setAdvancedFilters([]);
setSelectedUserId("");
setSelectedPromptId("");
}}
>
<ClearIcon className="h-4 w-4" />
Expand Down
135 changes: 135 additions & 0 deletions components/shared/prompt-combobox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"use client";

import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";

import { Check, ChevronsUpDown } from "lucide-react";
import { useParams } from "next/navigation";
import { useEffect, useState } from "react";

import { useQuery } from "react-query";
import { toast } from "sonner";

export function PromptCombobox({
setSelectedPrompt,
selectedPrompt,
}: {
setSelectedPrompt: (prompt: string) => void;
selectedPrompt?: string;
}) {
const project_id = useParams()?.project_id as string;
const [open, setOpen] = useState(false);
const [selectedPromptId, setSelectedPromptIdState] = useState(
selectedPrompt || ""
);
const [searchQuery, setSearchQuery] = useState("");
const [promptIds, setPromptIds] = useState<string[]>([]);
const [showLoader, setShowLoader] = useState(false);
const [internalSelectedPrompt, setInternalSelectedPrompt] =
useState(selectedPrompt);

const handleSelectPrompt = (currentValue: string) => {
const newPromptId = currentValue === selectedPromptId ? "" : currentValue;
setSelectedPromptIdState(newPromptId);
setSelectedPrompt(newPromptId);

setOpen(false);
};

useEffect(() => {
setSelectedPromptIdState(selectedPrompt || "");
setInternalSelectedPrompt(selectedPrompt || "");
}, [selectedPrompt]);

const fetchPromptIds = useQuery({
queryKey: ["fetch-prompt-ids-query", project_id],
queryFn: async () => {
const response = await fetch(`/api/prompt-ids?projectId=${project_id}`);

if (!response.ok) {
throw new Error("Failed to fetch prompt ids");
}
const result = await response.json();
return result;
},
onSuccess: (data: { promptIDs: any }) => {
setPromptIds(data?.promptIDs || []);
},
onError: (error) => {
setShowLoader(false);
toast.error("Failed to fetch prompt ids", {
description: error instanceof Error ? error.message : String(error),
});
},
});

if (fetchPromptIds.isLoading) {
return <div>Loading...</div>;
}

const onInputChange = (value: string) => {
setSearchQuery(value);
};

return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-[220px] justify-between"
>
{selectedPromptId ? selectedPromptId : "Filter by prompt id..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[220px] p-0">
<Command>
<CommandInput
placeholder="Search prompts..."
value={searchQuery}
onValueChange={onInputChange}
/>
<CommandEmpty>No attribute found.</CommandEmpty>
<CommandGroup>
{promptIds.map((id: string) => (
<CommandItem
key={id}
value={id}
onSelect={(currentValue) => {
setSelectedPromptIdState(
currentValue === selectedPromptId ? "" : currentValue
);
setSelectedPrompt(
currentValue === selectedPromptId ? "" : currentValue
);
handleSelectPrompt(currentValue);
setOpen(false);
}}
>
<Check
className={`mr-2 h-4 w-4 ${
selectedPromptId === id ? "opacity-100" : "opacity-0"
}`}
/>
{id}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
);
}
23 changes: 23 additions & 0 deletions lib/services/trace_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface ITraceService {
) => Promise<number>;
AddSpans: (spans: Span[], project_id: string) => Promise<void>;
GetUsersInProject: (project_id: string) => Promise<string[]>;
GetPromptsInProject: (project_id: string) => Promise<string[]>;
GetModelsInProject: (project_id: string) => Promise<string[]>;
}

Expand Down Expand Up @@ -122,6 +123,28 @@ export class TraceService implements ITraceService {
}
}

async GetPromptsInProject(project_id: string): Promise<string[]> {
try {
const tableExists = await this.client.checkTableExists(project_id);
if (!tableExists) {
return [];
}
const query = sql
.select([
`DISTINCT JSONExtractString(attributes, 'prompt_id') AS prompt_id`,
])
.from(project_id);
const result: any[] = await this.client.find(query);
return result
.map((row) => row.prompt_id)
.filter((prompt_id) => prompt_id !== "");
} catch (error) {
throw new Error(
`An error occurred while trying to get the prompts ${error}`
);
}
}

async GetModelsInProject(project_id: string): Promise<string[]> {
try {
const tableExists = await this.client.checkTableExists(project_id);
Expand Down