Skip to content

Commit 105df9d

Browse files
committed
feat: Add script for rewriting error messages using OpenAI API
1 parent 8464d10 commit 105df9d

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

scripts/rewrite_errors_llm.py

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# /// script
2+
# requires-python = ">=3.11"
3+
# dependencies = [
4+
# "aiohttp",
5+
# "dotenv",
6+
# "iofiles",
7+
# "json",
8+
# "openai",
9+
# ]
10+
# ///
11+
12+
13+
import asyncio
14+
import getpass
15+
import json
16+
import os
17+
from pathlib import Path
18+
19+
import aiofiles
20+
import aiohttp
21+
from dotenv import load_dotenv
22+
from openai import AsyncOpenAI
23+
24+
25+
def get_openai_api_key() -> str:
26+
# Try to get the API key from the environment variable
27+
if api_key := os.getenv("OPENAI_API_KEY"):
28+
return api_key
29+
30+
# Load environment variables from a .env file if not already loaded
31+
load_dotenv()
32+
if api_key := os.getenv("OPENAI_API_KEY"):
33+
return api_key
34+
35+
# Prompt the user for the API key as a last resort
36+
return getpass.getpass("Enter your OPENAI_API_KEY: ")
37+
38+
39+
# --- Config ---
40+
GUIDELINES_URL = "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/refs/heads/master/docs/messages-guidelines.md"
41+
INPUT_FILE = "errors.txt" # Supports either .txt (one per line) or .json (list)
42+
MODEL = "gpt-4"
43+
API_KEY = get_openai_api_key()
44+
45+
client = AsyncOpenAI(api_key=API_KEY)
46+
47+
# --- Functions ---
48+
49+
50+
async def fetch_guidelines() -> str:
51+
async with aiohttp.ClientSession() as session, session.get(GUIDELINES_URL) as resp:
52+
return await resp.text()
53+
54+
55+
async def load_messages(filepath: str) -> list[str]:
56+
path = Path(filepath)
57+
async with aiofiles.open(path) as f:
58+
content = await f.read()
59+
try:
60+
return json.loads(content)
61+
except json.JSONDecodeError:
62+
return [line.strip() for line in content.splitlines() if line.strip()]
63+
64+
65+
def build_system_prompt(guidelines: str) -> str:
66+
return f"""
67+
You are a technical writing assistant specialized in crafting professional error and warning messages.
68+
Your task is to rewrite the given error message to strictly adhere to the oSparc Simcore message guidelines.
69+
70+
Here are the guidelines:
71+
{guidelines}
72+
73+
Instructions:
74+
- Follow all the principles from the message guidelines.
75+
- Ensure the message is concise, user-focused, actionable, and avoids developer jargon.
76+
- If there is not enough context, ask a clarifying question.
77+
78+
Use this format only:
79+
If enough context:
80+
REWRITTEN: <rewritten message>
81+
If not enough context:
82+
NEED MORE INFO: <your clarifying question(s)>
83+
""".strip()
84+
85+
86+
async def rewrite_message(message: str, system_prompt: str, index: int) -> dict:
87+
try:
88+
response = await client.chat.completions.create(
89+
model=MODEL,
90+
messages=[
91+
{"role": "system", "content": system_prompt},
92+
{"role": "user", "content": f"ERROR: {message}"},
93+
],
94+
temperature=0,
95+
)
96+
return {
97+
"index": index,
98+
"original": message,
99+
"output": response.choices[0].message.content.strip(),
100+
}
101+
except Exception as e:
102+
return {"index": index, "original": message, "error": str(e)}
103+
104+
105+
# --- Main Flow ---
106+
107+
108+
async def main():
109+
guidelines = await fetch_guidelines()
110+
system_prompt = build_system_prompt(guidelines)
111+
messages = await load_messages(INPUT_FILE)
112+
113+
tasks = [
114+
rewrite_message(msg, system_prompt, i) for i, msg in enumerate(messages, 1)
115+
]
116+
results = await asyncio.gather(*tasks)
117+
118+
for result in results:
119+
print(f"\n[{result['index']}] Original: {result['original']}")
120+
if "output" in result:
121+
print(result["output"])
122+
else:
123+
print(f"ERROR: {result['error']}")
124+
125+
126+
if __name__ == "__main__":
127+
asyncio.run(main())

0 commit comments

Comments
 (0)