Art vandelay: Import & Export (#5053)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import type { Options as ClientOptions, TDataShape, Client } from './client';
|
||||
import type { AddSubRecipesData, AddSubRecipesResponses, AddSubRecipesErrors, ExtendPromptData, ExtendPromptResponses, ExtendPromptErrors, ResumeAgentData, ResumeAgentResponses, ResumeAgentErrors, UpdateSessionConfigData, UpdateSessionConfigResponses, UpdateSessionConfigErrors, StartAgentData, StartAgentResponses, StartAgentErrors, GetToolsData, GetToolsResponses, GetToolsErrors, UpdateAgentProviderData, UpdateAgentProviderResponses, UpdateAgentProviderErrors, UpdateRouterToolSelectorData, UpdateRouterToolSelectorResponses, UpdateRouterToolSelectorErrors, ReadAllConfigData, ReadAllConfigResponses, BackupConfigData, BackupConfigResponses, BackupConfigErrors, CreateCustomProviderData, CreateCustomProviderResponses, CreateCustomProviderErrors, RemoveCustomProviderData, RemoveCustomProviderResponses, RemoveCustomProviderErrors, GetExtensionsData, GetExtensionsResponses, GetExtensionsErrors, AddExtensionData, AddExtensionResponses, AddExtensionErrors, RemoveExtensionData, RemoveExtensionResponses, RemoveExtensionErrors, InitConfigData, InitConfigResponses, InitConfigErrors, UpsertPermissionsData, UpsertPermissionsResponses, UpsertPermissionsErrors, ProvidersData, ProvidersResponses, GetProviderModelsData, GetProviderModelsResponses, GetProviderModelsErrors, ReadConfigData, ReadConfigResponses, ReadConfigErrors, RecoverConfigData, RecoverConfigResponses, RecoverConfigErrors, RemoveConfigData, RemoveConfigResponses, RemoveConfigErrors, UpsertConfigData, UpsertConfigResponses, UpsertConfigErrors, ValidateConfigData, ValidateConfigResponses, ValidateConfigErrors, ConfirmPermissionData, ConfirmPermissionResponses, ConfirmPermissionErrors, ManageContextData, ManageContextResponses, ManageContextErrors, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, CreateRecipeData, CreateRecipeResponses, CreateRecipeErrors, DecodeRecipeData, DecodeRecipeResponses, DecodeRecipeErrors, DeleteRecipeData, DeleteRecipeResponses, DeleteRecipeErrors, EncodeRecipeData, EncodeRecipeResponses, EncodeRecipeErrors, ListRecipesData, ListRecipesResponses, ListRecipesErrors, ParseRecipeData, ParseRecipeResponses, ParseRecipeErrors, SaveRecipeData, SaveRecipeResponses, SaveRecipeErrors, ScanRecipeData, ScanRecipeResponses, ReplyData, ReplyResponses, ReplyErrors, CreateScheduleData, CreateScheduleResponses, CreateScheduleErrors, DeleteScheduleData, DeleteScheduleResponses, DeleteScheduleErrors, ListSchedulesData, ListSchedulesResponses, ListSchedulesErrors, UpdateScheduleData, UpdateScheduleResponses, UpdateScheduleErrors, InspectRunningJobData, InspectRunningJobResponses, InspectRunningJobErrors, KillRunningJobData, KillRunningJobResponses, PauseScheduleData, PauseScheduleResponses, PauseScheduleErrors, RunNowHandlerData, RunNowHandlerResponses, RunNowHandlerErrors, SessionsHandlerData, SessionsHandlerResponses, SessionsHandlerErrors, UnpauseScheduleData, UnpauseScheduleResponses, UnpauseScheduleErrors, ListSessionsData, ListSessionsResponses, ListSessionsErrors, GetSessionInsightsData, GetSessionInsightsResponses, GetSessionInsightsErrors, DeleteSessionData, DeleteSessionResponses, DeleteSessionErrors, GetSessionData, GetSessionResponses, GetSessionErrors, UpdateSessionDescriptionData, UpdateSessionDescriptionResponses, UpdateSessionDescriptionErrors, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesResponses, UpdateSessionUserRecipeValuesErrors, StatusData, StatusResponses } from './types.gen';
|
||||
import type { AddSubRecipesData, AddSubRecipesResponses, AddSubRecipesErrors, ExtendPromptData, ExtendPromptResponses, ExtendPromptErrors, ResumeAgentData, ResumeAgentResponses, ResumeAgentErrors, UpdateSessionConfigData, UpdateSessionConfigResponses, UpdateSessionConfigErrors, StartAgentData, StartAgentResponses, StartAgentErrors, GetToolsData, GetToolsResponses, GetToolsErrors, UpdateAgentProviderData, UpdateAgentProviderResponses, UpdateAgentProviderErrors, UpdateRouterToolSelectorData, UpdateRouterToolSelectorResponses, UpdateRouterToolSelectorErrors, ReadAllConfigData, ReadAllConfigResponses, BackupConfigData, BackupConfigResponses, BackupConfigErrors, CreateCustomProviderData, CreateCustomProviderResponses, CreateCustomProviderErrors, RemoveCustomProviderData, RemoveCustomProviderResponses, RemoveCustomProviderErrors, GetExtensionsData, GetExtensionsResponses, GetExtensionsErrors, AddExtensionData, AddExtensionResponses, AddExtensionErrors, RemoveExtensionData, RemoveExtensionResponses, RemoveExtensionErrors, InitConfigData, InitConfigResponses, InitConfigErrors, UpsertPermissionsData, UpsertPermissionsResponses, UpsertPermissionsErrors, ProvidersData, ProvidersResponses, GetProviderModelsData, GetProviderModelsResponses, GetProviderModelsErrors, ReadConfigData, ReadConfigResponses, ReadConfigErrors, RecoverConfigData, RecoverConfigResponses, RecoverConfigErrors, RemoveConfigData, RemoveConfigResponses, RemoveConfigErrors, UpsertConfigData, UpsertConfigResponses, UpsertConfigErrors, ValidateConfigData, ValidateConfigResponses, ValidateConfigErrors, ConfirmPermissionData, ConfirmPermissionResponses, ConfirmPermissionErrors, ManageContextData, ManageContextResponses, ManageContextErrors, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, CreateRecipeData, CreateRecipeResponses, CreateRecipeErrors, DecodeRecipeData, DecodeRecipeResponses, DecodeRecipeErrors, DeleteRecipeData, DeleteRecipeResponses, DeleteRecipeErrors, EncodeRecipeData, EncodeRecipeResponses, EncodeRecipeErrors, ListRecipesData, ListRecipesResponses, ListRecipesErrors, ParseRecipeData, ParseRecipeResponses, ParseRecipeErrors, SaveRecipeData, SaveRecipeResponses, SaveRecipeErrors, ScanRecipeData, ScanRecipeResponses, ReplyData, ReplyResponses, ReplyErrors, CreateScheduleData, CreateScheduleResponses, CreateScheduleErrors, DeleteScheduleData, DeleteScheduleResponses, DeleteScheduleErrors, ListSchedulesData, ListSchedulesResponses, ListSchedulesErrors, UpdateScheduleData, UpdateScheduleResponses, UpdateScheduleErrors, InspectRunningJobData, InspectRunningJobResponses, InspectRunningJobErrors, KillRunningJobData, KillRunningJobResponses, PauseScheduleData, PauseScheduleResponses, PauseScheduleErrors, RunNowHandlerData, RunNowHandlerResponses, RunNowHandlerErrors, SessionsHandlerData, SessionsHandlerResponses, SessionsHandlerErrors, UnpauseScheduleData, UnpauseScheduleResponses, UnpauseScheduleErrors, ListSessionsData, ListSessionsResponses, ListSessionsErrors, ImportSessionData, ImportSessionResponses, ImportSessionErrors, GetSessionInsightsData, GetSessionInsightsResponses, GetSessionInsightsErrors, DeleteSessionData, DeleteSessionResponses, DeleteSessionErrors, GetSessionData, GetSessionResponses, GetSessionErrors, UpdateSessionDescriptionData, UpdateSessionDescriptionResponses, UpdateSessionDescriptionErrors, ExportSessionData, ExportSessionResponses, ExportSessionErrors, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesResponses, UpdateSessionUserRecipeValuesErrors, StatusData, StatusResponses } from './types.gen';
|
||||
import { client as _heyApiClient } from './client.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
||||
@@ -457,6 +457,17 @@ export const listSessions = <ThrowOnError extends boolean = false>(options?: Opt
|
||||
});
|
||||
};
|
||||
|
||||
export const importSession = <ThrowOnError extends boolean = false>(options: Options<ImportSessionData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).post<ImportSessionResponses, ImportSessionErrors, ThrowOnError>({
|
||||
url: '/sessions/import',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getSessionInsights = <ThrowOnError extends boolean = false>(options?: Options<GetSessionInsightsData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<GetSessionInsightsResponses, GetSessionInsightsErrors, ThrowOnError>({
|
||||
url: '/sessions/insights',
|
||||
@@ -489,6 +500,13 @@ export const updateSessionDescription = <ThrowOnError extends boolean = false>(o
|
||||
});
|
||||
};
|
||||
|
||||
export const exportSession = <ThrowOnError extends boolean = false>(options: Options<ExportSessionData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).get<ExportSessionResponses, ExportSessionErrors, ThrowOnError>({
|
||||
url: '/sessions/{session_id}/export',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const updateSessionUserRecipeValues = <ThrowOnError extends boolean = false>(options: Options<UpdateSessionUserRecipeValuesData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).put<UpdateSessionUserRecipeValuesResponses, UpdateSessionUserRecipeValuesErrors, ThrowOnError>({
|
||||
url: '/sessions/{session_id}/user_recipe_values',
|
||||
|
||||
@@ -341,6 +341,10 @@ export type ImageContent = {
|
||||
mimeType: string;
|
||||
};
|
||||
|
||||
export type ImportSessionRequest = {
|
||||
json: string;
|
||||
};
|
||||
|
||||
export type InspectJobResponse = {
|
||||
processStartTime?: string | null;
|
||||
runningDurationSeconds?: number | null;
|
||||
@@ -2177,6 +2181,37 @@ export type ListSessionsResponses = {
|
||||
|
||||
export type ListSessionsResponse = ListSessionsResponses[keyof ListSessionsResponses];
|
||||
|
||||
export type ImportSessionData = {
|
||||
body: ImportSessionRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/sessions/import';
|
||||
};
|
||||
|
||||
export type ImportSessionErrors = {
|
||||
/**
|
||||
* Bad request - Invalid JSON
|
||||
*/
|
||||
400: unknown;
|
||||
/**
|
||||
* Unauthorized - Invalid or missing API key
|
||||
*/
|
||||
401: unknown;
|
||||
/**
|
||||
* Internal server error
|
||||
*/
|
||||
500: unknown;
|
||||
};
|
||||
|
||||
export type ImportSessionResponses = {
|
||||
/**
|
||||
* Session imported successfully
|
||||
*/
|
||||
200: Session;
|
||||
};
|
||||
|
||||
export type ImportSessionResponse = ImportSessionResponses[keyof ImportSessionResponses];
|
||||
|
||||
export type GetSessionInsightsData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
@@ -2312,6 +2347,42 @@ export type UpdateSessionDescriptionResponses = {
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type ExportSessionData = {
|
||||
body?: never;
|
||||
path: {
|
||||
/**
|
||||
* Unique identifier for the session
|
||||
*/
|
||||
session_id: string;
|
||||
};
|
||||
query?: never;
|
||||
url: '/sessions/{session_id}/export';
|
||||
};
|
||||
|
||||
export type ExportSessionErrors = {
|
||||
/**
|
||||
* Unauthorized - Invalid or missing API key
|
||||
*/
|
||||
401: unknown;
|
||||
/**
|
||||
* Session not found
|
||||
*/
|
||||
404: unknown;
|
||||
/**
|
||||
* Internal server error
|
||||
*/
|
||||
500: unknown;
|
||||
};
|
||||
|
||||
export type ExportSessionResponses = {
|
||||
/**
|
||||
* Session exported successfully
|
||||
*/
|
||||
200: string;
|
||||
};
|
||||
|
||||
export type ExportSessionResponse = ExportSessionResponses[keyof ExportSessionResponses];
|
||||
|
||||
export type UpdateSessionUserRecipeValuesData = {
|
||||
body: UpdateSessionUserRecipeValuesRequest;
|
||||
path: {
|
||||
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
Folder,
|
||||
Edit2,
|
||||
Trash2,
|
||||
Download,
|
||||
Upload,
|
||||
} from 'lucide-react';
|
||||
import { Card } from '../ui/card';
|
||||
import { Button } from '../ui/button';
|
||||
@@ -19,7 +21,14 @@ import { groupSessionsByDate, type DateGroup } from '../../utils/dateUtils';
|
||||
import { Skeleton } from '../ui/skeleton';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ConfirmationModal } from '../ui/ConfirmationModal';
|
||||
import { deleteSession, listSessions, Session, updateSessionDescription } from '../../api';
|
||||
import {
|
||||
deleteSession,
|
||||
exportSession,
|
||||
importSession,
|
||||
listSessions,
|
||||
Session,
|
||||
updateSessionDescription,
|
||||
} from '../../api';
|
||||
|
||||
interface EditSessionModalProps {
|
||||
session: Session | null;
|
||||
@@ -210,6 +219,8 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
}
|
||||
};
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const visibleDateGroups = useMemo(() => {
|
||||
return dateGroups.slice(0, visibleGroupsCount);
|
||||
}, [dateGroups, visibleGroupsCount]);
|
||||
@@ -427,14 +438,66 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
setSessionToDelete(null);
|
||||
}, []);
|
||||
|
||||
const handleExportSession = useCallback(async (session: Session, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const response = await exportSession({
|
||||
path: { session_id: session.id },
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
const json = response.data;
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${session.description || session.id}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
toast.success('Session exported successfully');
|
||||
}, []);
|
||||
|
||||
const handleImportClick = useCallback(() => {
|
||||
fileInputRef.current?.click();
|
||||
}, []);
|
||||
|
||||
const handleImportSession = useCallback(
|
||||
async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
const json = await file.text();
|
||||
await importSession({
|
||||
body: { json },
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
toast.success('Session imported successfully');
|
||||
await loadSessions();
|
||||
} catch (error) {
|
||||
toast.error(`Failed to import session: ${error}`);
|
||||
} finally {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
[loadSessions]
|
||||
);
|
||||
|
||||
const SessionItem = React.memo(function SessionItem({
|
||||
session,
|
||||
onEditClick,
|
||||
onDeleteClick,
|
||||
onExportClick,
|
||||
}: {
|
||||
session: Session;
|
||||
onEditClick: (session: Session) => void;
|
||||
onDeleteClick: (session: Session) => void;
|
||||
onExportClick: (session: Session, e: React.MouseEvent) => void;
|
||||
}) {
|
||||
const handleEditClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
@@ -456,6 +519,13 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
onSelectSession(session.id);
|
||||
}, [session.id]);
|
||||
|
||||
const handleExportClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
onExportClick(session, e);
|
||||
},
|
||||
[onExportClick, session]
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
onClick={handleCardClick}
|
||||
@@ -477,6 +547,13 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
>
|
||||
<Trash2 className="w-3 h-3 text-red-500 hover:text-red-600" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleExportClick}
|
||||
className="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer"
|
||||
title="Export session"
|
||||
>
|
||||
<Download className="w-3 h-3 text-textSubtle hover:text-textStandard" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
@@ -598,6 +675,7 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
session={session}
|
||||
onEditClick={handleEditSession}
|
||||
onDeleteClick={handleDeleteSession}
|
||||
onExportClick={handleExportSession}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -624,6 +702,15 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
<div className="flex flex-col page-transition">
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<h1 className="text-4xl font-light">Chat history</h1>
|
||||
<Button
|
||||
onClick={handleImportClick}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Upload className="w-4 h-4" />
|
||||
Import Session
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-sm text-text-muted mb-4">
|
||||
View and search your past conversations with Goose.
|
||||
@@ -701,6 +788,14 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
</div>
|
||||
</MainPanelLayout>
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".json"
|
||||
onChange={handleImportSession}
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
<EditSessionModal
|
||||
session={editingSession}
|
||||
isOpen={showEditModal}
|
||||
|
||||
Reference in New Issue
Block a user