Skip to content

Commit 42f2a5a

Browse files
committed
fix: json parse
1 parent 28a5b1b commit 42f2a5a

2 files changed

Lines changed: 80 additions & 9 deletions

File tree

‎.github/workflows/issue-auto-fix.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ jobs:
4747
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
4848
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
4949
ANTHROPIC_MODEL: ${{ secrets.ANTHROPIC_MODEL }}
50+
ANTHROPIC_DEFAULT_HAIKU_MODEL: claude-haiku-4.5
51+
ANTHROPIC_DEFAULT_SONNET_MODEL: claude-sonnet-4.6
52+
ANTHROPIC_DEFAULT_OPUS_MODEL: claude-opus-4.6
53+
ANTHROPIC_TIMEOUT_MS: 180000
54+
ANTHROPIC_MAX_RETRIES: 3
55+
ANTHROPIC_RETRY_BASE_MS: 2000
56+
ANTHROPIC_MAX_TOKENS: 2048
5057
ISSUE_NUMBER: ${{ github.event.issue.number }}
5158
ISSUE_TITLE: ${{ github.event.issue.title }}
5259
ISSUE_BODY: ${{ github.event.issue.body }}

‎scripts/auto-fix-issue.mjs‎

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,77 @@ function shouldRetry(status) {
6969
return status === 408 || status === 429 || status >= 500
7070
}
7171

72+
function tryParseJson(text) {
73+
try {
74+
return JSON.parse(text)
75+
} catch {
76+
return null
77+
}
78+
}
79+
80+
function extractLastJsonObject(text) {
81+
const cleaned = text
82+
.trim()
83+
.replace(/^```(?:json)?\s*/i, "")
84+
.replace(/\s*```$/, "")
85+
86+
const direct = tryParseJson(cleaned)
87+
if (direct && typeof direct === "object") {
88+
return direct
89+
}
90+
91+
let start = -1
92+
let depth = 0
93+
let inString = false
94+
let escaping = false
95+
const candidates = []
96+
97+
for (let i = 0; i < cleaned.length; i += 1) {
98+
const ch = cleaned[i]
99+
100+
if (escaping) {
101+
escaping = false
102+
continue
103+
}
104+
105+
if (ch === "\\") {
106+
escaping = true
107+
continue
108+
}
109+
110+
if (ch === '"') {
111+
inString = !inString
112+
continue
113+
}
114+
115+
if (inString) continue
116+
117+
if (ch === "{") {
118+
if (depth === 0) start = i
119+
depth += 1
120+
continue
121+
}
122+
123+
if (ch === "}") {
124+
if (depth === 0) continue
125+
depth -= 1
126+
if (depth === 0 && start >= 0) {
127+
candidates.push(cleaned.slice(start, i + 1))
128+
start = -1
129+
}
130+
}
131+
}
132+
133+
for (let i = candidates.length - 1; i >= 0; i -= 1) {
134+
const parsed = tryParseJson(candidates[i])
135+
if (parsed && typeof parsed === "object" && typeof parsed.patch === "string") {
136+
return parsed
137+
}
138+
}
139+
140+
return null
141+
}
142+
72143
async function requestClaude() {
73144
const endpoint = `${anthropicBaseUrl.replace(/\/$/, "")}/v1/messages`
74145

@@ -154,15 +225,8 @@ if (!outputText) {
154225
process.exit(1)
155226
}
156227

157-
const normalized = outputText
158-
.trim()
159-
.replace(/^```(?:json)?\s*/i, "")
160-
.replace(/\s*```$/, "")
161-
162-
let parsed
163-
try {
164-
parsed = JSON.parse(normalized)
165-
} catch {
228+
const parsed = extractLastJsonObject(outputText)
229+
if (!parsed) {
166230
console.error("Model output is not valid JSON")
167231
console.error(outputText)
168232
process.exit(1)

0 commit comments

Comments
 (0)