Improve DeepSeek compatibility (R1/reasoner support) and fix lint errors in main.py
Browse files
main.py
CHANGED
|
@@ -4,7 +4,7 @@ import json
|
|
| 4 |
import time
|
| 5 |
import boto3
|
| 6 |
from pathlib import Path
|
| 7 |
-
from typing import Dict, List, Optional, Tuple
|
| 8 |
from anthropic import Anthropic
|
| 9 |
import openai
|
| 10 |
from openai import OpenAI
|
|
@@ -244,7 +244,7 @@ class RetrieverEvent(Event):
|
|
| 244 |
class LLMAnalyzer:
|
| 245 |
"""Class for handling different LLM providers."""
|
| 246 |
|
| 247 |
-
def __init__(self, provider:
|
| 248 |
self.provider = provider
|
| 249 |
self.model_name = model_name
|
| 250 |
|
|
@@ -323,29 +323,36 @@ class LLMAnalyzer:
|
|
| 323 |
|
| 324 |
async def _analyze_with_deepseek(self, prompt: str) -> str:
|
| 325 |
"""Analyze text using DeepSeek."""
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
]
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
| 334 |
|
| 335 |
try:
|
| 336 |
-
|
| 337 |
-
model
|
| 338 |
-
messages
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
response_text = response.choices[0].message.content
|
| 343 |
|
| 344 |
# Verify and clean JSON
|
| 345 |
json_data = extract_json_from_text(response_text)
|
| 346 |
return json.dumps(json_data, ensure_ascii=False) if json_data else response_text
|
| 347 |
except Exception as e:
|
| 348 |
-
raise RuntimeError(f"Error in DeepSeek analysis: {str(e)}")
|
| 349 |
|
| 350 |
async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
|
| 351 |
"""Analyze text using Anthropic."""
|
|
@@ -451,8 +458,8 @@ class LLMAnalyzer:
|
|
| 451 |
class PrecedentAnalysisWorkflow(Workflow):
|
| 452 |
"""Workflow for analyzing legal precedents."""
|
| 453 |
|
| 454 |
-
def __init__(self, provider:
|
| 455 |
-
model_name:
|
| 456 |
super().__init__()
|
| 457 |
self.analyzer = LLMAnalyzer(provider, model_name)
|
| 458 |
|
|
@@ -670,29 +677,43 @@ def generate_legal_position(
|
|
| 670 |
client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com")
|
| 671 |
try:
|
| 672 |
print(f"[DEBUG] DeepSeek Generation - Model: {model_name}")
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
response_text = response.choices[0].message.content
|
| 683 |
-
print(f"[DEBUG] DeepSeek response length: {len(response_text)}")
|
| 684 |
|
| 685 |
json_response = extract_json_from_text(response_text)
|
| 686 |
if json_response and all(key in json_response for key in ["title", "text", "proceeding", "category"]):
|
| 687 |
return json_response
|
| 688 |
else:
|
| 689 |
-
print(f"[WARNING] Invalid JSON structure from DeepSeek. Text: {response_text[:300]}...")
|
| 690 |
raise ValueError("Invalid JSON structure")
|
| 691 |
except Exception as e:
|
| 692 |
print(f"[ERROR] DeepSeek generation/parsing failed: {e}")
|
| 693 |
return {
|
| 694 |
"title": "Автоматично сформований заголовок (DeepSeek)",
|
| 695 |
-
"text": response_text.strip() if 'response_text' in locals() else "Помилка при отриманні відповіді від DeepSeek",
|
| 696 |
"proceeding": "Не визначено",
|
| 697 |
"category": "Помилка API/Парсингу"
|
| 698 |
}
|
|
|
|
| 4 |
import time
|
| 5 |
import boto3
|
| 6 |
from pathlib import Path
|
| 7 |
+
from typing import Dict, List, Optional, Tuple, Any
|
| 8 |
from anthropic import Anthropic
|
| 9 |
import openai
|
| 10 |
from openai import OpenAI
|
|
|
|
| 244 |
class LLMAnalyzer:
|
| 245 |
"""Class for handling different LLM providers."""
|
| 246 |
|
| 247 |
+
def __init__(self, provider: Any, model_name: Any):
|
| 248 |
self.provider = provider
|
| 249 |
self.model_name = model_name
|
| 250 |
|
|
|
|
| 323 |
|
| 324 |
async def _analyze_with_deepseek(self, prompt: str) -> str:
|
| 325 |
"""Analyze text using DeepSeek."""
|
| 326 |
+
model_val = self.model_name.value if hasattr(self.model_name, "value") else str(self.model_name)
|
| 327 |
+
is_reasoning = "reasoner" in model_val.lower()
|
| 328 |
+
|
| 329 |
+
messages = []
|
| 330 |
+
if is_reasoning:
|
| 331 |
+
# DeepSeek R1 does not support system role, combine with user
|
| 332 |
+
messages.append(ChatMessage(role="user", content=f"{SYSTEM_PROMPT}\n\n{prompt}"))
|
| 333 |
+
else:
|
| 334 |
+
messages.append(ChatMessage(role="system", content=SYSTEM_PROMPT))
|
| 335 |
+
messages.append(ChatMessage(role="user", content=prompt))
|
| 336 |
|
| 337 |
try:
|
| 338 |
+
completion_params = {
|
| 339 |
+
"model": model_val,
|
| 340 |
+
"messages": [{"role": m.role, "content": m.content} for m in messages],
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
# Use JSON mode and temperature only for non-reasoning models
|
| 344 |
+
if not is_reasoning:
|
| 345 |
+
completion_params["response_format"] = {'type': 'json_object'}
|
| 346 |
+
completion_params["temperature"] = 0
|
| 347 |
+
|
| 348 |
+
response = self.client.chat.completions.create(**completion_params)
|
| 349 |
response_text = response.choices[0].message.content
|
| 350 |
|
| 351 |
# Verify and clean JSON
|
| 352 |
json_data = extract_json_from_text(response_text)
|
| 353 |
return json.dumps(json_data, ensure_ascii=False) if json_data else response_text
|
| 354 |
except Exception as e:
|
| 355 |
+
raise RuntimeError(f"Error in DeepSeek analysis ({model_val}): {str(e)}")
|
| 356 |
|
| 357 |
async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
|
| 358 |
"""Analyze text using Anthropic."""
|
|
|
|
| 458 |
class PrecedentAnalysisWorkflow(Workflow):
|
| 459 |
"""Workflow for analyzing legal precedents."""
|
| 460 |
|
| 461 |
+
def __init__(self, provider: Any = ModelProvider.OPENAI,
|
| 462 |
+
model_name: Any = AnalysisModelName.GPT4o_MINI):
|
| 463 |
super().__init__()
|
| 464 |
self.analyzer = LLMAnalyzer(provider, model_name)
|
| 465 |
|
|
|
|
| 677 |
client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com")
|
| 678 |
try:
|
| 679 |
print(f"[DEBUG] DeepSeek Generation - Model: {model_name}")
|
| 680 |
+
|
| 681 |
+
# Check for reasoning model (DeepSeek R1)
|
| 682 |
+
is_reasoning = "reasoner" in model_name.lower()
|
| 683 |
+
|
| 684 |
+
messages = []
|
| 685 |
+
if is_reasoning:
|
| 686 |
+
# R1 does not support system role, combine with user
|
| 687 |
+
combined_content = f"{system_prompt}\n\n{content}"
|
| 688 |
+
messages.append({"role": "user", "content": combined_content})
|
| 689 |
+
else:
|
| 690 |
+
messages.append({"role": "system", "content": system_prompt})
|
| 691 |
+
messages.append({"role": "user", "content": content})
|
| 692 |
+
|
| 693 |
+
completion_params = {
|
| 694 |
+
"model": model_name,
|
| 695 |
+
"messages": messages,
|
| 696 |
+
"max_tokens": MAX_TOKENS_CONFIG["deepseek"],
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
+
if not is_reasoning:
|
| 700 |
+
completion_params["temperature"] = GENERATION_TEMPERATURE
|
| 701 |
+
|
| 702 |
+
response = client.chat.completions.create(**completion_params)
|
| 703 |
response_text = response.choices[0].message.content
|
| 704 |
+
print(f"[DEBUG] DeepSeek response length: {len(response_text) if response_text else 0}")
|
| 705 |
|
| 706 |
json_response = extract_json_from_text(response_text)
|
| 707 |
if json_response and all(key in json_response for key in ["title", "text", "proceeding", "category"]):
|
| 708 |
return json_response
|
| 709 |
else:
|
| 710 |
+
print(f"[WARNING] Invalid JSON structure from DeepSeek. Text: {response_text[:300] if response_text else 'None'}...")
|
| 711 |
raise ValueError("Invalid JSON structure")
|
| 712 |
except Exception as e:
|
| 713 |
print(f"[ERROR] DeepSeek generation/parsing failed: {e}")
|
| 714 |
return {
|
| 715 |
"title": "Автоматично сформований заголовок (DeepSeek)",
|
| 716 |
+
"text": response_text.strip() if 'response_text' in locals() and response_text else f"Помилка при отриманні відповіді від DeepSeek: {str(e)}",
|
| 717 |
"proceeding": "Не визначено",
|
| 718 |
"category": "Помилка API/Парсингу"
|
| 719 |
}
|