"添加前端模板和运行代码模块"

This commit is contained in:
Codex
2026-06-01 13:43:09 +08:00
parent 24045274ad
commit 8092c4b1f8
587 changed files with 99761 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
import ky, { type KyResponse, type AfterResponseHook, type NormalizedOptions } from 'ky';
import { createParser, type EventSourceParser } from 'eventsource-parser';
export interface SSEOptions {
onData: (data: string) => void;
onEvent?: (event: any) => void;
onCompleted?: (error?: Error) => void;
onAborted?: () => void;
onReconnectInterval?: (interval: number) => void;
}
export const createSSEHook = (options: SSEOptions): AfterResponseHook => {
const hook: AfterResponseHook = async (request: Request, _options: NormalizedOptions, response: KyResponse) => {
if (!response.ok || !response.body) {
return;
}
let completed = false;
const innerOnCompleted = (error?: Error): void => {
if (completed) {
return;
}
completed = true;
options.onCompleted?.(error);
};
const isAborted = false;
const reader: ReadableStreamDefaultReader<Uint8Array> = response.body.getReader();
const decoder = new TextDecoder('utf8');
const parser: EventSourceParser = createParser({
onEvent: (event) => {
if (event.data) {
options.onEvent?.(event);
const dataArray: string[] = event.data.split('\n');
for (const data of dataArray) {
options.onData(data);
}
}
}
});
const read = (): void => {
if (isAborted) {
return;
}
reader.read().then((result: ReadableStreamReadResult<Uint8Array>) => {
if (result.done) {
innerOnCompleted();
return;
}
parser.feed(decoder.decode(result.value, { stream: true }));
read();
}).catch(error => {
if (request.signal.aborted) {
options.onAborted?.();
return;
}
innerOnCompleted(error as Error);
});
};
read();
return response;
};
return hook;
};
type ContentPart =
| { type: 'text'; text: string }
| { type: 'image_url'; image_url: { url: string } };
export interface ChatMessage {
role: 'user' | 'assistant' | 'system';
content: string | ContentPart[];
id?: string;
}
export interface ChatStreamOptions {
endpoint: string;
messages: ChatMessage[];
apiId: string;
onUpdate: (content: string) => void;
onComplete: () => void;
onError: (error: Error) => void;
signal?: AbortSignal;
}
export const sendChatStream = async (options: ChatStreamOptions): Promise<void> => {
const { messages, onUpdate, onComplete, onError, signal } = options;
let currentContent = '';
const sseHook = createSSEHook({
onData: (data: string) => {
try {
const parsed = JSON.parse(data);
if (parsed.choices?.[0]?.delta?.content) {
currentContent += parsed.choices[0].delta.content;
onUpdate(currentContent);
}
} catch {
console.warn('Failed to parse SSE data:', data);
}
},
onCompleted: (error?: Error) => {
if (error) {
onError(error);
} else {
onComplete();
}
},
onAborted: () => {
console.log('Stream aborted');
}
});
try {
await ky.post(options.endpoint, {
json: {
messages: messages.map(msg => ({
role: msg.role,
content: msg.content
})),
enable_thinking: false
},
headers: {
'X-App-Id': options.apiId,
'Content-Type': 'application/json'
},
signal,
hooks: {
afterResponse: [sseHook]
}
});
} catch (error) {
if (!signal?.aborted) {
onError(error as Error);
}
}
};

View File

@@ -0,0 +1,100 @@
import axios from 'axios';
const APP_ID = import.meta.env.VITE_APP_ID;
const apiClient = axios.create({
timeout: 60000,
headers: {
'Content-Type': 'application/json',
'X-App-Id': APP_ID
}
});
apiClient.interceptors.response.use(
(response) => response.data,
(error) => {
console.error('API 请求错误:', error);
if (error.response?.data?.status === 999) {
throw new Error(error.response.data.msg);
}
return Promise.reject(error);
}
);
export interface ImageGenerationParams {
prompt: string;
width?: number;
height?: number;
image_num?: number;
}
export interface ImageGenerationResponse {
status: number;
msg: string;
data: {
task_id: number;
};
}
export interface ImageQueryResponse {
status: number;
msg: string;
data: {
task_status: string;
task_progress: number;
sub_task_result_list?: Array<{
sub_task_status: string;
final_image_list?: Array<{
img_url: string;
width: number;
height: number;
}>;
}>;
};
}
export const generateImage = async (params: ImageGenerationParams): Promise<ImageGenerationResponse> => {
return apiClient.post(
'https://api-integrations.appmiaoda.com/app-7a7nlc9zki69/api-2bk93oeO9Nyz/v1/bce/wenxinworkshop/ai_custom_image_creation/v2/text2image',
{
prompt: params.prompt,
width: params.width || 768,
height: params.height || 1360,
image_num: params.image_num || 1
}
);
};
export const queryImageResult = async (taskId: number): Promise<ImageQueryResponse> => {
return apiClient.post(
'https://api-integrations.appmiaoda.com/app-7a7nlc9zki69/api-2jBYdN3A9Nyz/v1/bce/wenxinworkshop/ai_custom_image_creation/v2/query_task',
{
task_id: taskId
}
);
};
export const pollImageResult = async (
taskId: number,
maxAttempts = 30,
interval = 2000
): Promise<string> => {
for (let i = 0; i < maxAttempts; i++) {
const result = await queryImageResult(taskId);
if (result.data.task_status === 'SUCCESS') {
const imageUrl = result.data.sub_task_result_list?.[0]?.final_image_list?.[0]?.img_url;
if (imageUrl) {
return imageUrl;
}
}
if (result.data.task_status === 'FAILED') {
throw new Error('图片生成失败');
}
await new Promise(resolve => setTimeout(resolve, interval));
}
throw new Error('图片生成超时');
};