refactor: repartition server-side and client-side code
This commit is contained in:
57
app/.server/utils/api-response.ts
Normal file
57
app/.server/utils/api-response.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { json, type TypedResponse } from '@remix-run/node';
|
||||
import type { ApiResponse } from '~/types/global';
|
||||
|
||||
/**
|
||||
* 创建标准化的 API 响应
|
||||
*
|
||||
* @param data 响应数据
|
||||
* @param message 响应消息
|
||||
* @param status HTTP 状态码,默认为 200
|
||||
* @returns 标准化的 API 响应
|
||||
*/
|
||||
export function apiResponse<T = any>(
|
||||
status: number = 200,
|
||||
data?: T,
|
||||
message?: string,
|
||||
success: boolean = true,
|
||||
headers?: HeadersInit,
|
||||
): TypedResponse<ApiResponse<T>> {
|
||||
const finalSuccess = success ?? (status >= 200 && status < 300);
|
||||
|
||||
const responseBody: ApiResponse<T> = {
|
||||
success: finalSuccess,
|
||||
...(data !== undefined ? { data } : {}),
|
||||
...(message !== undefined ? { message } : {}),
|
||||
};
|
||||
|
||||
return json(responseBody, { status, headers });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成功的 API 响应
|
||||
* @param data 响应数据
|
||||
* @param message 成功消息
|
||||
* @returns 成功的 API 响应
|
||||
*/
|
||||
export function successResponse<T = any>(
|
||||
data?: T,
|
||||
message?: string,
|
||||
headers?: HeadersInit,
|
||||
): TypedResponse<ApiResponse<T>> {
|
||||
return apiResponse(200, data, message, true, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建错误的 API 响应
|
||||
* @param message 错误消息
|
||||
* @param status HTTP 状态码,默认为 400
|
||||
* @param data 额外的错误数据
|
||||
* @returns 错误的 API 响应
|
||||
*/
|
||||
export function errorResponse<T = any>(
|
||||
status: number = 400,
|
||||
errorDetails?: string,
|
||||
headers?: HeadersInit,
|
||||
): TypedResponse<ApiResponse<T>> {
|
||||
return apiResponse<T>(status, undefined, errorDetails, false, headers);
|
||||
}
|
||||
10
app/.server/utils/fetch.ts
Normal file
10
app/.server/utils/fetch.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
type CommonRequest = Omit<RequestInit, 'body'> & { body?: URLSearchParams | FormData | string };
|
||||
|
||||
export async function request(url: string, init?: CommonRequest) {
|
||||
const nodeFetch = await import('node-fetch');
|
||||
const https = await import('node:https');
|
||||
|
||||
const agent = url.startsWith('https') ? new https.Agent({ rejectUnauthorized: false }) : undefined;
|
||||
|
||||
return nodeFetch.default(url, { ...init, agent });
|
||||
}
|
||||
34
app/.server/utils/token.ts
Normal file
34
app/.server/utils/token.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { UIMessage, UIMessagePart } from 'ai';
|
||||
import { Tiktoken } from 'js-tiktoken/lite';
|
||||
import o200k_base from 'js-tiktoken/ranks/o200k_base';
|
||||
|
||||
const tiktoken = new Tiktoken(o200k_base);
|
||||
|
||||
export function encode(text: string) {
|
||||
return tiktoken.encode(text);
|
||||
}
|
||||
|
||||
export function decode(tokens: number[]) {
|
||||
return tiktoken.decode(tokens);
|
||||
}
|
||||
|
||||
export function approximatePromptTokenCount(messages: UIMessage[]): number {
|
||||
return messages.reduce((acc, message) => {
|
||||
return acc + approximateUsageFromContent(message.parts);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export function approximateUsageFromContent(parts: Array<UIMessagePart<any, any>>): number {
|
||||
let totalLength = 0;
|
||||
|
||||
for (const part of parts) {
|
||||
if (part.type === 'text') {
|
||||
totalLength += encode(part.text).length;
|
||||
}
|
||||
|
||||
if (part.type === 'reasoning') {
|
||||
totalLength += encode(part.text).length;
|
||||
}
|
||||
}
|
||||
return totalLength;
|
||||
}
|
||||
Reference in New Issue
Block a user