Fix OpenAI gpt-4.1 API parameters: role 'developer', max_completion_tokens, and reasoning effort support; update max_tokens in default config
Browse files- config/environments/default.yaml +4 -4
- main.py +56 -21
config/environments/default.yaml
CHANGED
|
@@ -23,10 +23,10 @@ llama_index:
|
|
| 23 |
# Generation Settings
|
| 24 |
generation:
|
| 25 |
max_tokens:
|
| 26 |
-
openai:
|
| 27 |
-
anthropic:
|
| 28 |
-
gemini:
|
| 29 |
-
deepseek:
|
| 30 |
max_tokens_analysis: 4000
|
| 31 |
temperature: 0.5
|
| 32 |
|
|
|
|
| 23 |
# Generation Settings
|
| 24 |
generation:
|
| 25 |
max_tokens:
|
| 26 |
+
openai: 2048
|
| 27 |
+
anthropic: 2048
|
| 28 |
+
gemini: 2048
|
| 29 |
+
deepseek: 2048
|
| 30 |
max_tokens_analysis: 4000
|
| 31 |
temperature: 0.5
|
| 32 |
|
main.py
CHANGED
|
@@ -244,7 +244,7 @@ class RetrieverEvent(Event):
|
|
| 244 |
class LLMAnalyzer:
|
| 245 |
"""Class for handling different LLM providers."""
|
| 246 |
|
| 247 |
-
def __init__(self, provider: ModelProvider, model_name: AnalysisModelName):
|
| 248 |
self.provider = provider
|
| 249 |
self.model_name = model_name
|
| 250 |
|
|
@@ -281,8 +281,15 @@ class LLMAnalyzer:
|
|
| 281 |
|
| 282 |
async def _analyze_with_openai(self, prompt: str, response_schema: dict) -> str:
|
| 283 |
"""Analyze text using OpenAI."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
messages = [
|
| 285 |
-
ChatMessage(role=
|
| 286 |
ChatMessage(role="user", content=prompt)
|
| 287 |
]
|
| 288 |
|
|
@@ -295,19 +302,24 @@ class LLMAnalyzer:
|
|
| 295 |
}
|
| 296 |
|
| 297 |
try:
|
| 298 |
-
|
| 299 |
-
model
|
| 300 |
-
messages
|
| 301 |
-
response_format
|
| 302 |
-
|
| 303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
response_text = response.choices[0].message.content
|
| 305 |
|
| 306 |
# Verify it's valid JSON
|
| 307 |
json_data = extract_json_from_text(response_text)
|
| 308 |
return json.dumps(json_data, ensure_ascii=False) if json_data else response_text
|
| 309 |
except Exception as e:
|
| 310 |
-
raise RuntimeError(f"Error in OpenAI analysis: {str(e)}")
|
| 311 |
|
| 312 |
async def _analyze_with_deepseek(self, prompt: str) -> str:
|
| 313 |
"""Analyze text using DeepSeek."""
|
|
@@ -604,29 +616,52 @@ def generate_legal_position(
|
|
| 604 |
client = OpenAI(api_key=OPENAI_API_KEY)
|
| 605 |
try:
|
| 606 |
print(f"[DEBUG] OpenAI Generation - Model: {model_name}")
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 616 |
response_text = response.choices[0].message.content
|
| 617 |
-
print(f"[DEBUG] OpenAI response length: {len(response_text)}")
|
| 618 |
|
| 619 |
json_response = extract_json_from_text(response_text)
|
| 620 |
if json_response and all(key in json_response for key in ["title", "text", "proceeding", "category"]):
|
| 621 |
return json_response
|
| 622 |
else:
|
| 623 |
-
print(f"[WARNING] Invalid JSON structure from OpenAI. Text: {response_text[:300]}...")
|
| 624 |
raise ValueError("Invalid JSON structure")
|
| 625 |
except Exception as e:
|
| 626 |
print(f"[ERROR] OpenAI generation/parsing failed: {e}")
|
| 627 |
return {
|
| 628 |
"title": "Автоматично сформований заголовок (OpenAI)",
|
| 629 |
-
"text": response_text.strip() if 'response_text' in locals() else "Помилка при отриманні
|
| 630 |
"proceeding": "Не визначено",
|
| 631 |
"category": "Помилка парсингу"
|
| 632 |
}
|
|
|
|
| 244 |
class LLMAnalyzer:
|
| 245 |
"""Class for handling different LLM providers."""
|
| 246 |
|
| 247 |
+
def __init__(self, provider: "ModelProvider", model_name: "AnalysisModelName"):
|
| 248 |
self.provider = provider
|
| 249 |
self.model_name = model_name
|
| 250 |
|
|
|
|
| 281 |
|
| 282 |
async def _analyze_with_openai(self, prompt: str, response_schema: dict) -> str:
|
| 283 |
"""Analyze text using OpenAI."""
|
| 284 |
+
# Determine model name and if it's a reasoning model
|
| 285 |
+
model_val = self.model_name.value if hasattr(self.model_name, "value") else str(self.model_name)
|
| 286 |
+
is_reasoning_model = any(m in model_val.lower() for m in ["gpt-4.1", "gpt-4.5", "o1", "o3"])
|
| 287 |
+
|
| 288 |
+
# Use developer role for newer models
|
| 289 |
+
role = "developer" if is_reasoning_model else "system"
|
| 290 |
+
|
| 291 |
messages = [
|
| 292 |
+
ChatMessage(role=role, content=SYSTEM_PROMPT),
|
| 293 |
ChatMessage(role="user", content=prompt)
|
| 294 |
]
|
| 295 |
|
|
|
|
| 302 |
}
|
| 303 |
|
| 304 |
try:
|
| 305 |
+
completion_params = {
|
| 306 |
+
"model": model_val,
|
| 307 |
+
"messages": [{"role": m.role, "content": m.content} for m in messages],
|
| 308 |
+
"response_format": response_format,
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
# Reasoning models usually require temperature=1.0 or none
|
| 312 |
+
if not is_reasoning_model:
|
| 313 |
+
completion_params["temperature"] = 0
|
| 314 |
+
|
| 315 |
+
response = self.client.chat.completions.create(**completion_params)
|
| 316 |
response_text = response.choices[0].message.content
|
| 317 |
|
| 318 |
# Verify it's valid JSON
|
| 319 |
json_data = extract_json_from_text(response_text)
|
| 320 |
return json.dumps(json_data, ensure_ascii=False) if json_data else response_text
|
| 321 |
except Exception as e:
|
| 322 |
+
raise RuntimeError(f"Error in OpenAI analysis ({model_val}): {str(e)}")
|
| 323 |
|
| 324 |
async def _analyze_with_deepseek(self, prompt: str) -> str:
|
| 325 |
"""Analyze text using DeepSeek."""
|
|
|
|
| 616 |
client = OpenAI(api_key=OPENAI_API_KEY)
|
| 617 |
try:
|
| 618 |
print(f"[DEBUG] OpenAI Generation - Model: {model_name}")
|
| 619 |
+
|
| 620 |
+
# Check for reasoning models (gpt-4.1, o1, etc.)
|
| 621 |
+
is_reasoning_model = any(m in model_name.lower() for m in ["gpt-4.1", "gpt-4.5", "o1", "o3"])
|
| 622 |
+
|
| 623 |
+
# Use developer role for newer models, system for others
|
| 624 |
+
role = "developer" if is_reasoning_model else "system"
|
| 625 |
+
|
| 626 |
+
messages = [
|
| 627 |
+
{"role": role, "content": system_prompt},
|
| 628 |
+
{"role": "user", "content": content},
|
| 629 |
+
]
|
| 630 |
+
|
| 631 |
+
# Parameters for chat completion
|
| 632 |
+
completion_params = {
|
| 633 |
+
"model": model_name,
|
| 634 |
+
"messages": messages,
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
# Set tokens based on model capabilities
|
| 638 |
+
if is_reasoning_model:
|
| 639 |
+
completion_params["max_completion_tokens"] = MAX_TOKENS_CONFIG["openai"]
|
| 640 |
+
else:
|
| 641 |
+
completion_params["max_tokens"] = MAX_TOKENS_CONFIG["openai"]
|
| 642 |
+
|
| 643 |
+
# Handle thinking/reasoning
|
| 644 |
+
if thinking_enabled and is_reasoning_model:
|
| 645 |
+
completion_params["reasoning_effort"] = thinking_level.lower()
|
| 646 |
+
# Reasoning models usually don't support temperature or it must be 1.0
|
| 647 |
+
else:
|
| 648 |
+
completion_params["temperature"] = GENERATION_TEMPERATURE
|
| 649 |
+
|
| 650 |
+
response = client.chat.completions.create(**completion_params)
|
| 651 |
response_text = response.choices[0].message.content
|
| 652 |
+
print(f"[DEBUG] OpenAI response length: {len(response_text) if response_text else 0}")
|
| 653 |
|
| 654 |
json_response = extract_json_from_text(response_text)
|
| 655 |
if json_response and all(key in json_response for key in ["title", "text", "proceeding", "category"]):
|
| 656 |
return json_response
|
| 657 |
else:
|
| 658 |
+
print(f"[WARNING] Invalid JSON structure from OpenAI. Text: {response_text[:300] if response_text else 'None'}...")
|
| 659 |
raise ValueError("Invalid JSON structure")
|
| 660 |
except Exception as e:
|
| 661 |
print(f"[ERROR] OpenAI generation/parsing failed: {e}")
|
| 662 |
return {
|
| 663 |
"title": "Автоматично сформований заголовок (OpenAI)",
|
| 664 |
+
"text": response_text.strip() if 'response_text' in locals() and response_text else f"Помилка при отриманні відповіді: {str(e)}",
|
| 665 |
"proceeding": "Не визначено",
|
| 666 |
"category": "Помилка парсингу"
|
| 667 |
}
|