Initial commit: EGBE chatbot template
Stripped from vercel/chatbot (Apache 2.0): - Dropped @vercel/* packages and AI Gateway - Removed artifacts feature (code/text/sheet/image side panel) - Switched AI provider to @ai-sdk/openai-compatible -> EGBE LiteLLM - Replaced Vercel Blob upload with data URLs - Dropped Redis resumable streams and rate limiter (in-memory now) - Added Dockerfile (Next.js standalone) + entrypoint that runs migrations - Wired DATABASE_URL, EGBE_AI_API_URL/KEY, NEXT_PUBLIC_BASE_URL for app-deploy.sh
This commit is contained in:
commit
3e21c2334c
129 changed files with 21913 additions and 0 deletions
77
lib/utils.ts
Normal file
77
lib/utils.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import type {
|
||||
UIMessage,
|
||||
UIMessagePart,
|
||||
} from 'ai';
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { formatISO } from 'date-fns';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { DBMessage } from '@/lib/db/schema';
|
||||
import { ChatbotError, type ErrorCode } from './errors';
|
||||
import type { ChatMessage, ChatTools, CustomUIDataTypes } from './types';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export const fetcher = async (url: string) => {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
const { code, cause } = await response.json();
|
||||
throw new ChatbotError(code as ErrorCode, cause);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export async function fetchWithErrorHandlers(
|
||||
input: RequestInfo | URL,
|
||||
init?: RequestInit,
|
||||
) {
|
||||
try {
|
||||
const response = await fetch(input, init);
|
||||
|
||||
if (!response.ok) {
|
||||
const { code, cause } = await response.json();
|
||||
throw new ChatbotError(code as ErrorCode, cause);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error: unknown) {
|
||||
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||
throw new ChatbotError('offline:chat');
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function generateUUID(): string {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
export function sanitizeText(text: string) {
|
||||
return text.replace('<has_function_call>', '');
|
||||
}
|
||||
|
||||
export function convertToUIMessages(messages: DBMessage[]): ChatMessage[] {
|
||||
return messages.map((message) => ({
|
||||
id: message.id,
|
||||
role: message.role as 'user' | 'assistant' | 'system',
|
||||
parts: message.parts as UIMessagePart<CustomUIDataTypes, ChatTools>[],
|
||||
metadata: {
|
||||
createdAt: formatISO(message.createdAt),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function getTextFromMessage(message: ChatMessage | UIMessage): string {
|
||||
return message.parts
|
||||
.filter((part) => part.type === 'text')
|
||||
.map((part) => (part as { type: 'text'; text: string}).text)
|
||||
.join('');
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue