This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
import { computed, reactive } from "vue"
|
||||
import { Status } from "../types"
|
||||
import { output, status } from "./code"
|
||||
|
||||
export const analyse = reactive({
|
||||
line: -1,
|
||||
message: "",
|
||||
})
|
||||
|
||||
export const showAnalyse = computed(
|
||||
() => ![Status.Accepted, Status.NotStarted].includes(status.value),
|
||||
)
|
||||
|
||||
function findError(line: string, language = "python") {
|
||||
const python: any = {
|
||||
"EOFError: EOF when reading a line": "需要在输入框填写输入信息",
|
||||
"SyntaxError: invalid character in identifier":
|
||||
"可能是单词拼写错误,可能是括号、引号写成中文的了",
|
||||
"SyntaxError: invalid syntax": "语法错误,不合法的语法",
|
||||
"SyntaxError: EOL while scanning string literal":
|
||||
"可能是这一行最后一个符号是中文的,或者引号、括号不匹配",
|
||||
"NameError: name '(.*?)' is not defined": (name: string) =>
|
||||
`命名错误,${name} 不知道是什么东西`,
|
||||
"IndentationError: expected an indented block": "缩进错误:这一行需要缩进",
|
||||
'TypeError: can only concatenate str \\(not "(.*?)"\\) to str':
|
||||
"文字和数字不能相加",
|
||||
}
|
||||
const c: any = {}
|
||||
const regex = { c, python }[language]
|
||||
let message = ""
|
||||
for (let r in regex) {
|
||||
const err = line.match(r)
|
||||
if (err) {
|
||||
if (typeof regex[r] === "function") {
|
||||
message = regex[r](err[1])
|
||||
} else {
|
||||
message = regex[r]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
export function analyzeError() {
|
||||
const line = output.value.match(/File "script.py", line (\d+)/)
|
||||
if (line) {
|
||||
analyse.line = parseInt(line[1])
|
||||
}
|
||||
const lines = output.value.split("\n")
|
||||
const lastLine = lines[lines.length - 1]
|
||||
analyse.message = findError(lastLine)
|
||||
}
|
||||
150
src/composables/analysis.ts
Normal file
150
src/composables/analysis.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { computed, ref } from "vue"
|
||||
import { Status } from "../types"
|
||||
import { output, status, code } from "./code"
|
||||
|
||||
export const analysis = ref("")
|
||||
export const loading = ref(false)
|
||||
|
||||
export async function getAIAnalysis() {
|
||||
analysis.value = ""
|
||||
// 使用 streaming 流式方式 fetch /ai/analysis 接口,传入 code 和 error_info
|
||||
const baseUrl = import.meta.env.PUBLIC_CODEAPI_URL
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/ai`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
code: code.value,
|
||||
language: code.language,
|
||||
error_info: output.value,
|
||||
}),
|
||||
})
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) return
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ""
|
||||
let eventLines: string[] = []
|
||||
let currentEvent: string | null = null
|
||||
|
||||
const flushEvent = () => {
|
||||
if (eventLines.length === 0) return false
|
||||
const raw = eventLines.join("\n")
|
||||
eventLines = []
|
||||
const event = currentEvent ?? "message"
|
||||
currentEvent = null
|
||||
|
||||
if (!raw) return false
|
||||
|
||||
let payload: unknown
|
||||
try {
|
||||
payload = JSON.parse(raw)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("无法解析 SSE 数据", error, raw)
|
||||
return false
|
||||
}
|
||||
|
||||
const data = (payload as { data?: string }).data ?? ""
|
||||
const message = (payload as { message?: string }).message ?? ""
|
||||
|
||||
if (event === "chunk") {
|
||||
appendContent(data)
|
||||
return false
|
||||
}
|
||||
|
||||
if (event === "error") {
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
if (message) {
|
||||
appendContent(`\n[错误] ${message}`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (event === "done") {
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
appendContent(data || message)
|
||||
return false
|
||||
}
|
||||
|
||||
const processLine = (line: string) => {
|
||||
if (line === "") {
|
||||
return flushEvent()
|
||||
}
|
||||
|
||||
if (line.startsWith("event:")) {
|
||||
currentEvent = line.slice(6).trimStart()
|
||||
return false
|
||||
}
|
||||
|
||||
if (!line.startsWith("data:")) return false
|
||||
|
||||
let value = line.slice(5)
|
||||
if (value.startsWith(" ")) {
|
||||
value = value.slice(1)
|
||||
}
|
||||
eventLines.push(value)
|
||||
return false
|
||||
}
|
||||
|
||||
const processBuffer = (final = false) => {
|
||||
const lines = buffer.split("\n")
|
||||
if (!final) {
|
||||
buffer = lines.pop() ?? ""
|
||||
} else {
|
||||
buffer = ""
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
const shouldStop = processLine(line)
|
||||
if (shouldStop) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (final) {
|
||||
return processLine("")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
const appendContent = (segment: string) => {
|
||||
if (!segment) return
|
||||
analysis.value += segment
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = (await reader.read()) as ReadableStreamReadResult<
|
||||
Uint8Array<ArrayBuffer>
|
||||
>
|
||||
if (done) break
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
if (processBuffer()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (processBuffer(true)) {
|
||||
return
|
||||
}
|
||||
} finally {
|
||||
if (loading.value) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const showAnalysis = computed(
|
||||
() => ![Status.Accepted, Status.NotStarted].includes(status.value),
|
||||
)
|
||||
Reference in New Issue
Block a user