Skip to content

Commit fab36de

Browse files
committed
Fix AI typescript script parsing of JSON
1 parent d9501a9 commit fab36de

1 file changed

Lines changed: 42 additions & 16 deletions

File tree

scripts/ai_generate_typescript.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,25 @@ def call_claude_api(api_key: str, change_summary: str, ts_context: Dict[str, str
136136
3. Type exports in models.ts for new models
137137
4. Cache implementations if model structures changed
138138
139-
Return JSON with this structure:
139+
Return ONLY a valid JSON object with this exact structure:
140140
{{
141141
"summary": "Brief description of what changed and why updates are needed",
142142
"files": {{
143143
"relative/path.ts": {{
144144
"action": "update",
145145
"changes": "Description of specific changes made",
146-
"content": "Complete updated file content"
146+
"content": "Complete updated file content as a single string"
147147
}}
148148
}},
149149
"breaking_changes": ["list any breaking changes"],
150150
"notes": "Additional notes for human reviewer"
151151
}}
152152
153-
Only include files that actually need changes. Keep existing patterns and style."""
153+
IMPORTANT:
154+
- Return ONLY the JSON object, no markdown formatting
155+
- The "content" field must be a single escaped string, not template literals
156+
- Use \\n for newlines in the content field
157+
- Only include files that actually need changes"""
154158

155159
try:
156160
with httpx.Client(timeout=60.0) as client:
@@ -175,25 +179,47 @@ def call_claude_api(api_key: str, change_summary: str, ts_context: Dict[str, str
175179
result = response.json()
176180
ai_response = result["content"][0]["text"]
177181

178-
# Extract JSON from response
182+
# Extract JSON from response - handle markdown code blocks
179183
import re
180-
json_match = re.search(r'\{.*\}', ai_response, re.DOTALL)
181-
if json_match:
184+
185+
# First try to find JSON in markdown code blocks
186+
code_block_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', ai_response, re.DOTALL)
187+
if code_block_match:
188+
json_content = code_block_match.group(1)
189+
else:
190+
# Fallback to finding raw JSON
191+
json_match = re.search(r'\{.*\}', ai_response, re.DOTALL)
192+
if json_match:
193+
json_content = json_match.group()
194+
else:
195+
print("❌ No JSON found in AI response")
196+
print(f"Raw response: {ai_response[:500]}")
197+
return {}
198+
199+
try:
200+
ai_json = json.loads(json_content)
201+
print(f"✅ AI generation successful")
202+
print(f"Summary: {ai_json.get('summary', 'No summary')}")
203+
print(f"Files to update: {len(ai_json.get('files', {}))}")
204+
return ai_json
205+
206+
except json.JSONDecodeError as e:
207+
print(f"❌ JSON parse error: {e}")
208+
print(f"Trying to clean JSON content...")
209+
210+
# Try to clean up common JSON issues
182211
try:
183-
ai_json = json.loads(json_match.group())
184-
print(f"✅ AI generation successful")
212+
# Remove any trailing commas and fix common issues
213+
cleaned = re.sub(r',\s*}', '}', json_content)
214+
cleaned = re.sub(r',\s*]', ']', cleaned)
215+
ai_json = json.loads(cleaned)
216+
print(f"✅ AI generation successful (after cleanup)")
185217
print(f"Summary: {ai_json.get('summary', 'No summary')}")
186218
print(f"Files to update: {len(ai_json.get('files', {}))}")
187219
return ai_json
188-
189-
except json.JSONDecodeError as e:
190-
print(f"❌ JSON parse error: {e}")
191-
print(f"Raw response: {ai_response[:500]}")
220+
except json.JSONDecodeError:
221+
print(f"Raw response: {ai_response[:1000]}")
192222
return {}
193-
else:
194-
print("❌ No JSON found in AI response")
195-
print(f"Raw response: {ai_response[:500]}")
196-
return {}
197223
else:
198224
print(f"❌ Claude API error: {response.status_code}")
199225
print(response.text[:500])

0 commit comments

Comments
 (0)