chore: 提取配置项到统一配置中心
This commit is contained in:
parent
a799afebab
commit
18ac423352
|
|
@ -0,0 +1,41 @@
|
||||||
|
// ============================================================
|
||||||
|
// 全局配置中心
|
||||||
|
// 所有 API 地址、密钥、模型名称等配置统一在此管理
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
// ── GRS AI(作文批改 / 试题分析)──
|
||||||
|
export const GRS_API_KEY = "sk-6ba4b57a0b034d9388f7a7a2d477d637";
|
||||||
|
|
||||||
|
// 作文批改接口
|
||||||
|
export const ESSAY_API_URL = "https://grsai.dakka.com.cn/v1/draw/nano-banana";
|
||||||
|
export const ESSAY_MODEL = "nano-banana-pro";
|
||||||
|
export const ESSAY_PROMPT = "请对这篇英语作文进行批改,标注语法错误、拼写错误和表达不当之处,并给出修改建议,使用中文批注";
|
||||||
|
|
||||||
|
// 试题分析接口
|
||||||
|
export const EXAM_API_URL = "https://grsaiapi.com/v1/chat/completions";
|
||||||
|
export const EXAM_MODEL = "gemini-3.1-pro";
|
||||||
|
export const EXAM_TEMPERATURE = 0.7;
|
||||||
|
export const EXAM_MAX_TOKENS = 2048;
|
||||||
|
|
||||||
|
// ── 豆包 TTS(发音 / 口语对话)──
|
||||||
|
export const DOUBAO_APP_ID = "2542859186";
|
||||||
|
export const DOUBAO_ACCESS_TOKEN = "a4h5fT3cVlBi82u93iEQlqT3c4MP6_8V";
|
||||||
|
export const DOUBAO_RESOURCE_ID = "seed-tts-2.0";
|
||||||
|
export const DOUBAO_TTS_API_PATH = "/tts-api/api/v3/tts/unidirectional";
|
||||||
|
export const DOUBAO_AUDIO_FORMAT = "mp3";
|
||||||
|
export const DOUBAO_SAMPLE_RATE = 24000;
|
||||||
|
|
||||||
|
// ── 火山引擎 Ark 大模型(口语对话)──
|
||||||
|
export const ARK_API_KEY = "";
|
||||||
|
export const ARK_MODEL = "doubao-pro-4k";
|
||||||
|
export const ARK_API_PATH = "/ark-api/api/v3/chat/completions";
|
||||||
|
export const ARK_MAX_TOKENS = 200;
|
||||||
|
export const ARK_HISTORY_LIMIT = 20; // 保留最近对话条数
|
||||||
|
|
||||||
|
// ── 外部链接 ──
|
||||||
|
export const LISTENING_AUDIO_URL = "https://jz5k88k7vv.coze.site"; // 听力音频生成
|
||||||
|
|
||||||
|
// ── 文件上传限制 ──
|
||||||
|
export const IMAGE_MAX_SIZE_MB = 10;
|
||||||
|
export const IMAGE_ALLOWED_TYPES_ESSAY = ["image/jpeg", "image/jpg", "image/png"];
|
||||||
|
export const IMAGE_ALLOWED_TYPES_EXAM = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
|
||||||
|
|
@ -2,11 +2,17 @@
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import {
|
||||||
|
GRS_API_KEY,
|
||||||
|
ESSAY_API_URL,
|
||||||
|
ESSAY_PROMPT,
|
||||||
|
IMAGE_MAX_SIZE_MB,
|
||||||
|
IMAGE_ALLOWED_TYPES_ESSAY,
|
||||||
|
} from "@/config/index.js";
|
||||||
|
|
||||||
const API_KEY = "sk-6ba4b57a0b034d9388f7a7a2d477d637";
|
const API_KEY = GRS_API_KEY;
|
||||||
const API_URL = "https://grsai.dakka.com.cn/v1/draw/nano-banana";
|
const API_URL = ESSAY_API_URL;
|
||||||
const PROMPT =
|
const PROMPT = ESSAY_PROMPT;
|
||||||
"请对这篇英语作文进行批改,标注语法错误、拼写错误和表达不当之处,并给出修改建议,使用中文批注";
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
@ -36,13 +42,13 @@ const readFileAsBase64 = (file) =>
|
||||||
|
|
||||||
const handleFile = async (file) => {
|
const handleFile = async (file) => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
const allowed = ["image/jpeg", "image/jpg", "image/png"];
|
const allowed = IMAGE_ALLOWED_TYPES_ESSAY;
|
||||||
if (!allowed.includes(file.type)) {
|
if (!allowed.includes(file.type)) {
|
||||||
errorMsg.value = "仅支持 JPG / PNG 格式的图片";
|
errorMsg.value = "仅支持 JPG / PNG 格式的图片";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (file.size > 10 * 1024 * 1024) {
|
if (file.size > IMAGE_MAX_SIZE_MB * 1024 * 1024) {
|
||||||
errorMsg.value = "图片大小不能超过 10MB";
|
errorMsg.value = `图片大小不能超过 ${IMAGE_MAX_SIZE_MB}MB`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
errorMsg.value = "";
|
errorMsg.value = "";
|
||||||
|
|
@ -85,7 +91,7 @@ const startCorrection = async () => {
|
||||||
imageSize: "1K",
|
imageSize: "1K",
|
||||||
urls: [originalBase64.value],
|
urls: [originalBase64.value],
|
||||||
webHook: "",
|
webHook: "",
|
||||||
shutProgress: true,
|
shutProgress: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -107,7 +113,8 @@ const startCorrection = async () => {
|
||||||
// 接口返回 SSE 格式文本,形如 "data: {...}\n\n",需去掉 "data: " 前缀再解析
|
// 接口返回 SSE 格式文本,形如 "data: {...}\n\n",需去掉 "data: " 前缀再解析
|
||||||
let data;
|
let data;
|
||||||
try {
|
try {
|
||||||
const raw = typeof res.data === "string" ? res.data : JSON.stringify(res.data);
|
const raw =
|
||||||
|
typeof res.data === "string" ? res.data : JSON.stringify(res.data);
|
||||||
const jsonStr = raw.replace(/^data:\s*/m, "").trim();
|
const jsonStr = raw.replace(/^data:\s*/m, "").trim();
|
||||||
data = JSON.parse(jsonStr);
|
data = JSON.parse(jsonStr);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ import { ref, computed, onUnmounted } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
|
import { GRS_API_KEY, EXAM_API_URL, EXAM_MODEL, EXAM_TEMPERATURE, EXAM_MAX_TOKENS, IMAGE_MAX_SIZE_MB, IMAGE_ALLOWED_TYPES_EXAM } from "@/config/index.js";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// ── API 配置(请替换为实际的 Gemini API Key 和地址)──
|
const API_KEY = GRS_API_KEY;
|
||||||
const API_KEY = "sk-6ba4b57a0b034d9388f7a7a2d477d637";
|
const API_URL = EXAM_API_URL;
|
||||||
const API_URL = "https://grsaiapi.com/v1/chat/completions";
|
|
||||||
|
|
||||||
const SYSTEM_PROMPT = `你是一位专业的英语教师,请对以下英语试题进行深度解析。
|
const SYSTEM_PROMPT = `你是一位专业的英语教师,请对以下英语试题进行深度解析。
|
||||||
请严格按照以下格式输出,每个部分用对应标题开头:
|
请严格按照以下格式输出,每个部分用对应标题开头:
|
||||||
|
|
@ -117,13 +118,13 @@ const readFileAsBase64 = (file) =>
|
||||||
|
|
||||||
const handleFile = async (file) => {
|
const handleFile = async (file) => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
const allowed = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
|
const allowed = IMAGE_ALLOWED_TYPES_EXAM;
|
||||||
if (!allowed.includes(file.type)) {
|
if (!allowed.includes(file.type)) {
|
||||||
errorMsg.value = "仅支持 JPG / PNG / WebP 格式的图片";
|
errorMsg.value = "仅支持 JPG / PNG / WebP 格式的图片";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (file.size > 10 * 1024 * 1024) {
|
if (file.size > IMAGE_MAX_SIZE_MB * 1024 * 1024) {
|
||||||
errorMsg.value = "图片大小不能超过 10MB";
|
errorMsg.value = `图片大小不能超过 ${IMAGE_MAX_SIZE_MB}MB`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
errorMsg.value = "";
|
errorMsg.value = "";
|
||||||
|
|
@ -158,13 +159,13 @@ const buildRequestBody = (imageBase64 = null, mimeType = "image/jpeg") => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
model: "gemini-3.1-pro",
|
model: EXAM_MODEL,
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "system", content: SYSTEM_PROMPT },
|
{ role: "system", content: SYSTEM_PROMPT },
|
||||||
{ role: "user", content: userContent },
|
{ role: "user", content: userContent },
|
||||||
],
|
],
|
||||||
temperature: 0.7,
|
temperature: EXAM_TEMPERATURE,
|
||||||
max_tokens: 2048,
|
max_tokens: EXAM_MAX_TOKENS,
|
||||||
stream: true,
|
stream: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
import { LISTENING_AUDIO_URL } from "@/config/index.js";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const features = ref([
|
const features = ref([
|
||||||
|
|
@ -10,7 +11,7 @@ const features = ref([
|
||||||
desc: "输入听力文本,一键调用 AI 合成标准英语考试音频,支持多种题型场景,快速生成可用于备考的模拟听力材料。",
|
desc: "输入听力文本,一键调用 AI 合成标准英语考试音频,支持多种题型场景,快速生成可用于备考的模拟听力材料。",
|
||||||
class: "card-1",
|
class: "card-1",
|
||||||
icon: "audio",
|
icon: "audio",
|
||||||
url: "https://jz5k88k7vv.coze.site",
|
url: LISTENING_AUDIO_URL,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onUnmounted } from "vue";
|
import { ref, computed, onUnmounted } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
import { DOUBAO_APP_ID, DOUBAO_ACCESS_TOKEN, DOUBAO_RESOURCE_ID, DOUBAO_TTS_API_PATH, DOUBAO_AUDIO_FORMAT, DOUBAO_SAMPLE_RATE } from "@/config/index.js";
|
||||||
// ==========================================
|
|
||||||
// 豆包 (Volcengine) TTS API 配置项 (V3 API)
|
|
||||||
// 请在此处填写您在火山引擎申请的具体参数
|
|
||||||
// ==========================================
|
|
||||||
const DOUBAO_APP_ID = "2542859186";
|
|
||||||
const DOUBAO_ACCESS_TOKEN = "a4h5fT3cVlBi82u93iEQlqT3c4MP6_8V";
|
|
||||||
// V3 API 需要 Resource ID,通常为 volc.service_type.10029 (针对1.0) 或者 seed-tts-2.0
|
|
||||||
const DOUBAO_RESOURCE_ID = "seed-tts-2.0";
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const textInput = ref("");
|
const textInput = ref("");
|
||||||
|
|
@ -417,11 +408,11 @@ const handleDemoPlay = async (id) => {
|
||||||
req_params: {
|
req_params: {
|
||||||
text: voice.demo_text,
|
text: voice.demo_text,
|
||||||
speaker: voice.voice_type,
|
speaker: voice.voice_type,
|
||||||
audio_params: { format: "mp3", sample_rate: 24000 },
|
audio_params: { format: DOUBAO_AUDIO_FORMAT, sample_rate: DOUBAO_SAMPLE_RATE },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch("/tts-api/api/v3/tts/unidirectional", {
|
const response = await fetch(DOUBAO_TTS_API_PATH, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -599,14 +590,14 @@ const handleGenerate = async () => {
|
||||||
text: textInput.value,
|
text: textInput.value,
|
||||||
speaker: selectedVoiceData.voice_type,
|
speaker: selectedVoiceData.voice_type,
|
||||||
audio_params: {
|
audio_params: {
|
||||||
format: "mp3",
|
format: DOUBAO_AUDIO_FORMAT,
|
||||||
sample_rate: 24000,
|
sample_rate: DOUBAO_SAMPLE_RATE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// 调用配置的代理路径避免跨域 (指向 https://openspeech.bytedance.com/api/v3/tts/unidirectional)
|
// 调用配置的代理路径避免跨域 (指向 https://openspeech.bytedance.com/api/v3/tts/unidirectional)
|
||||||
const response = await fetch("/tts-api/api/v3/tts/unidirectional", {
|
const response = await fetch(DOUBAO_TTS_API_PATH, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, nextTick, onUnmounted } from "vue";
|
import { ref, computed, nextTick, onUnmounted } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
import { DOUBAO_APP_ID, DOUBAO_ACCESS_TOKEN, DOUBAO_RESOURCE_ID, DOUBAO_TTS_API_PATH, DOUBAO_AUDIO_FORMAT, DOUBAO_SAMPLE_RATE, ARK_API_KEY, ARK_MODEL, ARK_API_PATH, ARK_MAX_TOKENS, ARK_HISTORY_LIMIT } from "@/config/index.js";
|
||||||
// ==========================================
|
|
||||||
// 豆包 TTS API 配置(复用 Pronunciation.vue)
|
|
||||||
// ==========================================
|
|
||||||
const DOUBAO_APP_ID = "2542859186";
|
|
||||||
const DOUBAO_ACCESS_TOKEN = "a4h5fT3cVlBi82u93iEQlqT3c4MP6_8V";
|
|
||||||
const DOUBAO_RESOURCE_ID = "seed-tts-2.0";
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 字节跳动 Ark 大模型 API 配置
|
|
||||||
// 请填写您的 Ark API Key,留空则使用预设回复
|
|
||||||
// ==========================================
|
|
||||||
const ARK_API_KEY = "";
|
|
||||||
const ARK_MODEL = "doubao-pro-4k";
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
@ -175,11 +162,11 @@ const synthesizeAndPlay = async (text, msgId) => {
|
||||||
req_params: {
|
req_params: {
|
||||||
text,
|
text,
|
||||||
speaker: selectedVoice.value,
|
speaker: selectedVoice.value,
|
||||||
audio_params: { format: "mp3", sample_rate: 24000 },
|
audio_params: { format: DOUBAO_AUDIO_FORMAT, sample_rate: DOUBAO_SAMPLE_RATE },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch("/tts-api/api/v3/tts/unidirectional", {
|
const response = await fetch(DOUBAO_TTS_API_PATH, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -285,21 +272,21 @@ const replayMessage = (msg) => {
|
||||||
|
|
||||||
// 调用 Ark 大模型
|
// 调用 Ark 大模型
|
||||||
const callArkAPI = async (history) => {
|
const callArkAPI = async (history) => {
|
||||||
const response = await fetch("/ark-api/api/v3/chat/completions", {
|
const response = await fetch(ARK_API_PATH, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": `Bearer ${ARK_API_KEY}`,
|
"Authorization": `Bearer ${ARK_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: ARK_MODEL,
|
model: ARK_MODEL,
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "system", content: currentScene.value.systemPrompt },
|
{ role: "system", content: currentScene.value.systemPrompt },
|
||||||
...history,
|
...history,
|
||||||
],
|
],
|
||||||
max_tokens: 200,
|
max_tokens: ARK_MAX_TOKENS,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (!response.ok) throw new Error(`Ark HTTP ${response.status}`);
|
if (!response.ok) throw new Error(`Ark HTTP ${response.status}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data.choices[0].message.content.trim();
|
return data.choices[0].message.content.trim();
|
||||||
|
|
@ -334,7 +321,7 @@ const sendMessage = async () => {
|
||||||
// 构建对话历史(最近20条)
|
// 构建对话历史(最近20条)
|
||||||
const history = messages.value
|
const history = messages.value
|
||||||
.filter((m) => !m.isLoading && m.content)
|
.filter((m) => !m.isLoading && m.content)
|
||||||
.slice(-20)
|
.slice(-ARK_HISTORY_LIMIT)
|
||||||
.map((m) => ({ role: m.role, content: m.content }));
|
.map((m) => ({ role: m.role, content: m.content }));
|
||||||
|
|
||||||
let replyText = "";
|
let replyText = "";
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
}
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/tts-api': {
|
'/tts-api': {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue