File size: 2,887 Bytes
0017280
6d5b557
0017280
05c53af
 
 
 
 
 
 
 
 
 
 
0017280
106354e
68a6539
6d5b557
0017280
 
91410f8
05c53af
 
 
 
 
91410f8
 
 
 
 
 
 
05c53af
91410f8
 
 
 
 
 
 
 
 
 
549355f
91410f8
 
 
 
 
 
fbd18c8
 
 
 
 
 
 
 
 
0017280
 
112b4f4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""OpenAI LLM provider."""
SUPPORTED_MODELS = ["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano"]

# Models that don't support temperature or legacy max_tokens parameter.
# These require max_completion_tokens and the "developer" role instead of "system".
_RESTRICTED_PARAM_MODELS = {"gpt-5", "gpt-5-mini", "o1", "o3", "o4", "o4-mini"}
_RESTRICTED_PARAM_PREFIXES = ("o1-", "o3-", "o4-", "gpt-5-")


def _is_restricted_model(model: str) -> bool:
    """Check if the model restricts temperature/max_tokens/system role."""
    return model in _RESTRICTED_PARAM_MODELS or model.startswith(_RESTRICTED_PARAM_PREFIXES)


def generate(system_prompt: str, user_message: str, api_key: str,
             model: str = None, temperature: float = 0.0,
             max_tokens: int = 8192) -> str:
    model = model or "gpt-4.1"
    from openai import OpenAI
    client = OpenAI(api_key=api_key)
    try:
        if _is_restricted_model(model):
            # These models don't support temperature or the "system" role.
            # Reasoning models need a larger budget for thinking tokens.
            is_reasoning = model in ("o1", "o3", "o4", "o4-mini") or model.startswith(("o1-", "o3-", "o4-"))
            budget = min(max(max_tokens * 4, 4096), 128000) if is_reasoning else max_tokens
            messages = [
                {"role": "developer", "content": system_prompt},
                {"role": "user", "content": user_message},
            ]
            response = client.chat.completions.create(
                model=model,
                messages=messages,
                max_completion_tokens=budget,
            )
        else:
            messages = [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_message},
            ]
            response = client.chat.completions.create(
                model=model,
                messages=messages,
                temperature=temperature,
                max_completion_tokens=max_tokens,
            )
        if not response.choices:
            return ""
        return response.choices[0].message.content or ""
    except Exception as e:
        error_type = type(e).__name__
        error_msg = str(e)
        if "auth" in error_type.lower() or "authentication" in error_type.lower():
            raise RuntimeError(f"OpenAI authentication failed. Check your API key.") from e
        elif "rate" in error_type.lower() or "429" in error_msg:
            raise RuntimeError(f"OpenAI rate limit exceeded. Wait a moment and try again.") from e
        elif "model" in error_msg.lower() or "not found" in error_msg.lower():
            raise RuntimeError(f"OpenAI model error: {error_msg}") from e
        else:
            raise RuntimeError(f"OpenAI API error ({error_type}): {error_msg}") from e

def list_models(api_key: str) -> list[str]:
    return list(SUPPORTED_MODELS)