mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 14:10:15 -07:00
064de8dc1e
Chats are exported to paste.gg, are unlisted by default, and expire in 30 days by default. The user is also provided with the deletion key which will be only shown at the time of creation, and it's needed to take down the paste. Rendering looks quite great, including code and turns.
203 lines
5.5 KiB
TypeScript
203 lines
5.5 KiB
TypeScript
// noinspection ExceptionCaughtLocallyJS
|
|
|
|
import { ApiExportBody, ApiExportResponse } from '../pages/api/export';
|
|
import { DConversation } from '@/lib/store-chats';
|
|
import { SystemPurposes } from '@/lib/data';
|
|
|
|
|
|
/**
|
|
* Primitive rendering of a Conversation to Markdown
|
|
*/
|
|
function conversationToMarkdown(conversation: DConversation) {
|
|
|
|
// const title =
|
|
// `# ${conversation.name || 'Conversation'}\n` +
|
|
// (new Date(conversation.created)).toLocaleString() + '\n\n';
|
|
|
|
return conversation.messages.map(message => {
|
|
let sender: string = message.sender;
|
|
switch (message.role) {
|
|
case 'system':
|
|
sender = '✨ System message';
|
|
break;
|
|
case 'assistant':
|
|
sender = `Assistant ${prettyBaseModel(message.modelId)}`.trim();
|
|
const purpose = conversation.systemPurposeId || null;
|
|
if (purpose && purpose in SystemPurposes)
|
|
sender = `${SystemPurposes[purpose]?.symbol || ''} ${sender}`.trim();
|
|
break;
|
|
case 'user':
|
|
sender = '👤 You';
|
|
break;
|
|
}
|
|
return `### ${sender}\n\n${message.text}\n\n`;
|
|
}).join('---\n\n');
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the origin of the current page
|
|
*/
|
|
function getOrigin() {
|
|
let origin = (typeof window !== 'undefined') ? window.location.href : '';
|
|
if (!origin || origin.includes('//localhost'))
|
|
origin = 'https://github.com/enricoros/nextjs-chatgpt-app';
|
|
origin = origin.replace('https://', '');
|
|
if (origin.endsWith('/'))
|
|
origin = origin.slice(0, -1);
|
|
return origin;
|
|
}
|
|
|
|
/**
|
|
* Exports a markdown rendering of the conversation to a service of choice
|
|
*
|
|
* **Called by the UI to render the data and post it to the API**
|
|
*
|
|
* NOTE: we are calling our own API here, which in turn calls the paste.gg API. We do this
|
|
* because the browser wouldn't otherwise allow us to perform a CORS to paste.gg
|
|
*
|
|
* @param gg Only one service for now
|
|
* @param conversation The conversation to export
|
|
*/
|
|
export async function exportConversation(gg: 'paste.gg', conversation: DConversation): Promise<ApiExportResponse | null> {
|
|
|
|
const body: ApiExportBody = {
|
|
to: gg,
|
|
title: '🤖💬 Chat Conversation',
|
|
fileContent: conversationToMarkdown(conversation),
|
|
fileName: 'my-chat.md',
|
|
origin: getOrigin(),
|
|
};
|
|
|
|
try {
|
|
|
|
const response = await fetch('/api/export', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const paste: ApiExportResponse = await response.json();
|
|
|
|
if (paste.type === 'success') {
|
|
// we log this to the console for extra safety
|
|
console.log('Data from your export to \'paste.gg\'', paste);
|
|
return paste;
|
|
}
|
|
|
|
if (paste.type === 'error')
|
|
throw new Error(`Failed to send the paste: ${paste.error}`);
|
|
}
|
|
|
|
throw new Error(`Failed to export conversation: ${response.status}: ${response.statusText}`);
|
|
|
|
} catch (error) {
|
|
console.error('Export issue', error);
|
|
alert(`Export issue: ${error}`);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
export function prettyBaseModel(model: string | undefined): string {
|
|
if (!model) return '';
|
|
if (model.startsWith('gpt-4')) return 'gpt-4';
|
|
if (model.startsWith('gpt-3.5-turbo')) return '3.5 Turbo';
|
|
return model;
|
|
}
|
|
|
|
|
|
/**
|
|
* Post a paste to paste.gg
|
|
* [called by the API]
|
|
* - API description: https://github.com/ascclemens/paste/blob/master/api.md
|
|
*
|
|
* @param title Title of the paste
|
|
* @param fileName File with extension, e.g. 'conversation.md'
|
|
* @param fileContent Textual content (e.g. markdown text)
|
|
* @param origin the URL of the page that generated the paste
|
|
* @param expireDays Number of days after which the paste will expire (0 = never expires, default = 30)
|
|
*/
|
|
export async function postToPasteGG(title: string, fileName: string, fileContent: string, origin: string, expireDays: number = 30): Promise<PasteGGAPI.PasteResponse> {
|
|
|
|
// Default: expire in 30 days
|
|
let expires = null;
|
|
if (expireDays && expireDays >= 1) {
|
|
const expirationDate = new Date();
|
|
expirationDate.setDate(expirationDate.getDate() + expireDays);
|
|
expires = expirationDate.toISOString();
|
|
}
|
|
|
|
const pasteData: PasteGGAPI.Paste = {
|
|
name: title,
|
|
description: `Generated by ${origin} 🚀`,
|
|
visibility: 'unlisted',
|
|
...(expires && { expires }),
|
|
files: [{
|
|
name: fileName,
|
|
content: {
|
|
format: 'text',
|
|
value: fileContent,
|
|
},
|
|
}],
|
|
};
|
|
|
|
const response = await fetch('https://api.paste.gg/v1/pastes', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(pasteData),
|
|
});
|
|
|
|
if (response.ok)
|
|
return await response.json() as PasteGGAPI.PasteResponse;
|
|
|
|
console.error(`Failed to create paste: ${response.status}`, response);
|
|
throw new Error(`Failed to create paste: ${response.statusText}`);
|
|
|
|
}
|
|
|
|
|
|
export namespace PasteGGAPI {
|
|
export interface Paste {
|
|
name?: string;
|
|
description?: string;
|
|
visibility?: 'public' | 'unlisted' | 'private';
|
|
expires?: string;
|
|
files: File[];
|
|
}
|
|
|
|
interface File {
|
|
name?: string;
|
|
content: Content;
|
|
}
|
|
|
|
interface Content {
|
|
format: 'text' | 'base64' | 'gzip' | 'xz';
|
|
highlight_language?: string | null;
|
|
value: string;
|
|
}
|
|
|
|
export type PasteResponse = {
|
|
status: 'success'
|
|
result: Paste & {
|
|
id: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
files: {
|
|
id: string;
|
|
name: string;
|
|
highlight_language?: string | null;
|
|
}[];
|
|
deletion_key?: string;
|
|
};
|
|
} | {
|
|
status: 'error';
|
|
error: string;
|
|
message?: string;
|
|
}
|
|
|
|
} |