feat: transition to a fully frontend architecture by removing backend dependencies and updating configuration for local deployment

This commit is contained in:
HouYunFei
2026-06-16 12:56:49 +08:00
parent 8a66524ea7
commit 7f0199da59
36 changed files with 571 additions and 1773 deletions
+20 -13
View File
@@ -1,6 +1,6 @@
import axios from "axios";
import { buildApiUrl, type AiConfig } from "@/stores/use-config-store";
import { buildApiUrl, resolveModelRequestConfig, type AiConfig, type ModelChannel } from "@/stores/use-config-store";
import { nanoid } from "nanoid";
import { dataUrlToFile } from "@/lib/image-utils";
import { buildImageReferencePromptText } from "@/lib/image-reference-prompt";
@@ -184,15 +184,16 @@ function withSystemMessage(config: AiConfig, messages: ChatCompletionMessage[])
}
export async function requestGeneration(config: AiConfig, prompt: string) {
const requestConfig = resolveModelRequestConfig(config, config.model || config.imageModel);
const n = Math.max(1, Math.min(15, Math.floor(Math.abs(Number(config.count)) || 1)));
const quality = normalizeQuality(config.quality);
const requestSize = resolveRequestSize(quality, config.size);
try {
const response = await axios.post<ImageApiResponse>(
aiApiUrl(config, "/images/generations"),
aiApiUrl(requestConfig, "/images/generations"),
{
model: config.model,
prompt: withSystemPrompt(config, prompt),
model: requestConfig.model,
prompt: withSystemPrompt(requestConfig, prompt),
n,
...(quality ? { quality } : {}),
...(requestSize ? { size: requestSize } : {}),
@@ -200,7 +201,7 @@ export async function requestGeneration(config: AiConfig, prompt: string) {
output_format: IMAGE_OUTPUT_FORMAT,
},
{
headers: aiHeaders(config, "application/json"),
headers: aiHeaders(requestConfig, "application/json"),
},
);
const images = parseImagePayload(response.data);
@@ -211,13 +212,14 @@ export async function requestGeneration(config: AiConfig, prompt: string) {
}
export async function requestEdit(config: AiConfig, prompt: string, references: ReferenceImage[], mask?: ReferenceImage) {
const requestConfig = resolveModelRequestConfig(config, config.model || config.imageModel);
const n = Math.max(1, Math.min(15, Math.floor(Math.abs(Number(config.count)) || 1)));
const quality = normalizeQuality(config.quality);
const requestSize = resolveRequestSize(quality, config.size);
const requestPrompt = buildImageReferencePromptText(prompt, references);
const formData = new FormData();
formData.set("model", config.model);
formData.set("prompt", withSystemPrompt(config, requestPrompt));
formData.set("model", requestConfig.model);
formData.set("prompt", withSystemPrompt(requestConfig, requestPrompt));
formData.set("n", String(n));
formData.set("response_format", "b64_json");
formData.set("output_format", IMAGE_OUTPUT_FORMAT);
@@ -232,7 +234,7 @@ export async function requestEdit(config: AiConfig, prompt: string, references:
if (mask) formData.set("mask", dataUrlToFile(mask));
try {
const response = await axios.post<ImageApiResponse>(aiApiUrl(config, "/images/edits"), formData, { headers: aiHeaders(config) });
const response = await axios.post<ImageApiResponse>(aiApiUrl(requestConfig, "/images/edits"), formData, { headers: aiHeaders(requestConfig) });
const images = parseImagePayload(response.data);
return images;
} catch (error) {
@@ -241,21 +243,22 @@ export async function requestEdit(config: AiConfig, prompt: string, references:
}
export async function requestImageQuestion(config: AiConfig, messages: ChatCompletionMessage[], onDelta: (text: string) => void) {
const requestConfig = resolveModelRequestConfig(config, config.model || config.textModel);
let buffer = "";
let answer = "";
let processedLength = 0;
try {
const response = await axios.post(
aiApiUrl(config, "/chat/completions"),
aiApiUrl(requestConfig, "/chat/completions"),
{
model: config.model,
messages: withSystemMessage(config, messages),
model: requestConfig.model,
messages: withSystemMessage(requestConfig, messages),
stream: true,
},
{
headers: {
...aiHeaders(config, "application/json"),
...aiHeaders(requestConfig, "application/json"),
} as Record<string, string>,
responseType: "text",
onDownloadProgress: (event) => {
@@ -301,7 +304,7 @@ export async function requestImageQuestion(config: AiConfig, messages: ChatCompl
return answer || "没有返回内容";
}
export async function fetchImageModels(config: AiConfig) {
export async function fetchImageModels(config: Pick<AiConfig, "baseUrl" | "apiKey">) {
try {
const response = await axios.get<{ data?: Array<{ id?: string }>; error?: { message?: string } }>(buildApiUrl(config.baseUrl, "/models"), {
headers: {
@@ -316,3 +319,7 @@ export async function fetchImageModels(config: AiConfig) {
throw new Error(readAxiosError(error, "读取模型失败"));
}
}
export async function fetchChannelModels(channel: ModelChannel) {
return fetchImageModels({ baseUrl: channel.baseUrl, apiKey: channel.apiKey });
}