Skip to content

Commit

Permalink
Adds frontend annotations workflow
Browse files Browse the repository at this point in the history
This allows for:
1. Creating + editing annotations in the app view
2. Viewing all annotations for a project
3. Editing in the project view
  • Loading branch information
elijahbenizzy committed Oct 11, 2024
1 parent b657f87 commit 7ab9513
Show file tree
Hide file tree
Showing 16 changed files with 11,538 additions and 5,754 deletions.
16,747 changes: 11,073 additions & 5,674 deletions telemetry/ui/package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion telemetry/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@headlessui/react": "^2.0.0-alpha.4",
"@headlessui/react": "^2.1.9",
"@heroicons/react": "^2.1.1",
"@microsoft/fetch-event-source": "^2.0.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@tisoap/react-flow-smart-edge": "^3.0.0",
"@types/fuse": "^2.6.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.82",
"@types/react": "^18.2.56",
Expand All @@ -19,6 +20,7 @@
"@uiw/react-json-view": "^2.0.0-alpha.12",
"clsx": "^2.1.0",
"elkjs": "^0.9.1",
"fuse.js": "^7.0.0",
"heroicons": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -27,6 +29,7 @@
"react-query": "^3.39.3",
"react-router-dom": "^6.22.1",
"react-scripts": "5.0.1",
"react-select": "^5.8.1",
"react-syntax-highlighter": "^15.5.0",
"reactflow": "^11.10.4",
"remark-gfm": "^4.0.0",
Expand Down
2 changes: 2 additions & 0 deletions telemetry/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Counter } from './examples/Counter';
import { EmailAssistantWithTelemetry } from './examples/EmailAssistant';
import { StreamingChatbotWithTelemetry } from './examples/StreamingChatbot';
import { AdminView } from './components/routes/AdminView';
import { AnnotationsViewContainer } from './components/routes/app/AnnotationsView';

/**
* Basic application. We have an AppContainer -- this has a breadcrumb and a sidebar.
Expand Down Expand Up @@ -37,6 +38,7 @@ const App = () => {
<Route path="/project/:projectId" element={<AppList />} />
<Route path="/project/:projectId/:partitionKey" element={<AppList />} />
<Route path="/project/:projectId/:partitionKey/:appId" element={<AppViewContainer />} />
<Route path="/annotations/:projectId/" element={<AnnotationsViewContainer />} />
<Route path="/demos/counter" element={<Counter />} />
<Route path="/demos/chatbot" element={<ChatbotWithTelemetry />} />
<Route path="/demos/streaming-chatbot" element={<StreamingChatbotWithTelemetry />} />
Expand Down
5 changes: 5 additions & 0 deletions telemetry/ui/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';

export type { ActionModel } from './models/ActionModel';
export type { AnnotationCreate } from './models/AnnotationCreate';
export { AnnotationDataPointer } from './models/AnnotationDataPointer';
export type { AnnotationObservation } from './models/AnnotationObservation';
export type { AnnotationOut } from './models/AnnotationOut';
export type { AnnotationUpdate } from './models/AnnotationUpdate';
export type { ApplicationLogs } from './models/ApplicationLogs';
export type { ApplicationModel } from './models/ApplicationModel';
export type { ApplicationPage } from './models/ApplicationPage';
Expand Down
94 changes: 94 additions & 0 deletions telemetry/ui/src/api/services/DefaultService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { AnnotationCreate } from '../models/AnnotationCreate';
import type { AnnotationOut } from '../models/AnnotationOut';
import type { AnnotationUpdate } from '../models/AnnotationUpdate';
import type { ApplicationLogs } from '../models/ApplicationLogs';
import type { ApplicationPage } from '../models/ApplicationPage';
import type { BackendSpec } from '../models/BackendSpec';
Expand Down Expand Up @@ -124,6 +127,97 @@ export class DefaultService {
}
});
}
/**
* Create Annotation
* @param projectId
* @param appId
* @param partitionKey
* @param sequenceId
* @param requestBody
* @returns AnnotationOut Successful Response
* @throws ApiError
*/
public static createAnnotationApiV0ProjectIdAppIdPartitionKeySequenceIdAnnotationsPost(
projectId: string,
appId: string,
partitionKey: string,
sequenceId: number,
requestBody: AnnotationCreate
): CancelablePromise<AnnotationOut> {
return __request(OpenAPI, {
method: 'POST',
url: '/api/v0/{project_id}/{app_id}/{partition_key}/{sequence_id}/annotations',
path: {
project_id: projectId,
app_id: appId,
partition_key: partitionKey,
sequence_id: sequenceId
},
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`
}
});
}
/**
* Update Annotation
* @param projectId
* @param annotationId
* @param requestBody
* @returns AnnotationOut Successful Response
* @throws ApiError
*/
public static updateAnnotationApiV0ProjectIdAnnotationIdUpdateAnnotationsPut(
projectId: string,
annotationId: number,
requestBody: AnnotationUpdate
): CancelablePromise<AnnotationOut> {
return __request(OpenAPI, {
method: 'PUT',
url: '/api/v0/{project_id}/{annotation_id}/update_annotations',
path: {
project_id: projectId,
annotation_id: annotationId
},
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`
}
});
}
/**
* Get Annotations
* @param projectId
* @param appId
* @param partitionKey
* @param stepSequenceId
* @returns AnnotationOut Successful Response
* @throws ApiError
*/
public static getAnnotationsApiV0ProjectIdAnnotationsGet(
projectId: string,
appId?: string | null,
partitionKey?: string | null,
stepSequenceId?: number | null
): CancelablePromise<Array<AnnotationOut>> {
return __request(OpenAPI, {
method: 'GET',
url: '/api/v0/{project_id}/annotations',
path: {
project_id: projectId
},
query: {
app_id: appId,
partition_key: partitionKey,
step_sequence_id: stepSequenceId
},
errors: {
422: `Validation Error`
}
});
}
/**
* Ready
* @returns boolean Successful Response
Expand Down
48 changes: 35 additions & 13 deletions telemetry/ui/src/components/common/chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,61 @@ const chipColorMap = {
fork: 'bg-dwdarkblue/80',
spawn: 'bg-purple-600',
span: 'bg-yellow-500/80',
attribute: 'bg-gray-500',
attribute: 'bg-teal-700',
state: 'bg-dwlightblue',
error: 'bg-dwred',
action: 'bg-dwlightblue/90',
stream: 'bg-dwlightblue/90',
first_item_stream: 'bg-pink-400',
end_stream: 'bg-pink-400',
llm: 'bg-gray-400/50',
metric: 'bg-gray-400/50'
metric: 'bg-gray-400/50',
tag: 'bg-gray-700/50',
annotateDataPointerAttribute: 'bg-teal-700', // same as attribute
annotateDataPointerState: 'bg-dwlightblue'
};

export type ChipType = keyof typeof chipColorMap;
/**
* Chip component for displaying a label with a background color.
* This currently centralizes the color mapping for the chips.
* This makes it easy to centralize meaning across the repository
* although it breaks encapsulation.
*/

export const Chip = (props: {
label: string;
chipType: ChipType;
className?: string;
onClick?: (e: React.MouseEvent) => void;
}) => {
const bgColor = chipColorMap[props.chipType];
// Function to generate a hash code from a string
const stringToHash = (str: string) => {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
hash |= 0; // Convert to 32bit integer
}
return hash;
};

// Function to generate a color from a hash
const hashToColor = (hash: number) => {
// Generate RGB values between 64 and 192 for more vibrant colors
const r = 64 + (Math.abs(hash) % 128);
const g = 64 + (Math.abs(hash >> 8) % 128);
const b = 64 + (Math.abs(hash >> 16) % 128);

return `rgb(${r}, ${g}, ${b})`;
};

// Generate color if chipType is 'tag'
const bgStyle = props.chipType === 'tag' ? hashToColor(stringToHash(props.label)) : undefined;
const bgColor = props.chipType === 'tag' ? '' : chipColorMap[props.chipType];
const clickable = props.onClick !== undefined;

return (
<div
className={`relative grid select-none items-center whitespace-nowrap rounded-lg
p-1 px-3 font-sans text-xs font-semibold text-white ${clickable ? 'cursor-pointer hover:underline' : ''} ${bgColor} ${
props.className ? props.className : ''
}`}
p-1 px-3 font-sans text-xs font-semibold text-white ${bgColor} ${clickable ? 'cursor-pointer hover:underline' : ''} ${props.className ? props.className : ''}`}
style={{ backgroundColor: bgStyle }}
onClick={props.onClick}
>
<span className="">{props.label}</span>
<span>{props.label}</span>
</div>
);
};
Expand Down
29 changes: 25 additions & 4 deletions telemetry/ui/src/components/routes/ProjectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { Loading } from '../common/loading';
import { DateDisplay } from '../common/dates';
import { Chip } from '../common/chip';
import { LinkText } from '../common/href';
import { useNavigate } from 'react-router-dom';
import { Link, useNavigate } from 'react-router-dom';
import { FaRegEdit } from 'react-icons/fa';

/**
* Table of a project list. Uses the tailwind catalyst component.
* This does not load or fetch any data, just renders it
*/
export const ProjectListTable = (props: { projects: Project[] }) => {
export const ProjectListTable = (props: { projects: Project[]; includeAnnotations: boolean }) => {
const navigate = useNavigate();
const projectsCopy = [...props.projects];
const projectsSorted = projectsCopy.sort((a, b) => {
Expand All @@ -26,6 +27,7 @@ export const ProjectListTable = (props: { projects: Project[] }) => {
<TableHeader>Last Run</TableHeader>
<TableHeader>Link</TableHeader>
<TableHeader>App Runs</TableHeader>
{props.includeAnnotations && <TableHeader className="w-10">Annotations</TableHeader>}
<TableHeader></TableHeader>
</TableRow>
</TableHead>
Expand Down Expand Up @@ -67,6 +69,20 @@ export const ProjectListTable = (props: { projects: Project[] }) => {
/>
</TableCell>
<TableCell>{project.num_apps}</TableCell>
{props.includeAnnotations && (
<TableCell>
<Link to={`/annotations/${project.id}`}>
<FaRegEdit
className="hover:underline hover:scale-125"
onClick={(e) => {
navigate(`/annotations/${project.id}`);
e.stopPropagation();
e.preventDefault();
}}
></FaRegEdit>
</Link>
</TableCell>
)}
</TableRow>
);
})}
Expand All @@ -80,11 +96,16 @@ export const ProjectListTable = (props: { projects: Project[] }) => {
*/
export const ProjectList = () => {
const { data, error } = useQuery('projects', DefaultService.getProjectsApiV0ProjectsGet);
const { data: backendSpec } = useQuery(['backendSpec'], () =>
DefaultService.getAppSpecApiV0MetadataAppSpecGet().then((response) => {
return response;
})
);
if (error) return <div>Error loading projects</div>;
if (data === undefined) return <Loading />;
if (data === undefined || backendSpec === undefined) return <Loading />;
return (
<div className="">
<ProjectListTable projects={data} />
<ProjectListTable projects={data} includeAnnotations={backendSpec?.supports_annotations} />
</div>
);
};
2 changes: 2 additions & 0 deletions telemetry/ui/src/components/routes/app/AnnotationsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,8 @@ export const AnnotationsViewContainer = () => {
const { data, refetch } = useQuery(['annotations', projectId], () =>
DefaultService.getAnnotationsApiV0ProjectIdAnnotationsGet(projectId as string)
);
// dummy value as this will not be linked to if annotations are not supported

if (data === undefined || backendSpec === undefined) return <Loading />;
return (
<AnnotationsTable
Expand Down
Loading

0 comments on commit 7ab9513

Please sign in to comment.