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