Spaces:
Running
Running
feat: Add thinking parameters to Analysis mode and update models list in README
Browse files- README.md +6 -6
- interface.py +63 -2
- main.py +67 -18
README.md
CHANGED
|
@@ -64,15 +64,15 @@ python_version: "3.11"
|
|
| 64 |
## 🎯 Підтримка AI провайдерів
|
| 65 |
|
| 66 |
### Для генерації:
|
| 67 |
-
- **OpenAI**: GPT-5.2 (NEW! з reasoning), GPT-
|
| 68 |
-
- **Anthropic**: Claude 4.
|
| 69 |
-
- **Google**: Gemini 3
|
| 70 |
- **DeepSeek**: DeepSeek Chat
|
| 71 |
|
| 72 |
### Для аналізу:
|
| 73 |
-
- **OpenAI**: GPT-5.2 (NEW! з reasoning)
|
| 74 |
-
- **Anthropic**: Claude 4.
|
| 75 |
-
- **Google**: Gemini 3
|
| 76 |
- **DeepSeek**: DeepSeek Chat
|
| 77 |
|
| 78 |
### 🆕 GPT-5.2 - Нова модель з reasoning!
|
|
|
|
| 64 |
## 🎯 Підтримка AI провайдерів
|
| 65 |
|
| 66 |
### Для генерації:
|
| 67 |
+
- **OpenAI**: GPT-5.4, GPT-5.3 Chat Latest, GPT-5.2 (NEW! з reasoning), GPT-4o Mini Fine-Tuned (кастомні моделі)
|
| 68 |
+
- **Anthropic**: Claude Opus 4.6, Claude Sonnet 4.6 (з підтримкою Extended Thinking), Claude Haiku 4.5
|
| 69 |
+
- **Google**: Gemini 3 Flash, Gemini 3 Pro (з підтримкою Thinking Mode)
|
| 70 |
- **DeepSeek**: DeepSeek Chat
|
| 71 |
|
| 72 |
### Для аналізу:
|
| 73 |
+
- **OpenAI**: GPT-5.4, GPT-5.3 Chat Latest, GPT-5.2 (NEW! з reasoning)
|
| 74 |
+
- **Anthropic**: Claude Opus 4.6, Claude Sonnet 4.6 (з підтримкою Extended Thinking), Claude Haiku 4.5
|
| 75 |
+
- **Google**: Gemini 3 Flash, Gemini 3 Pro (з підтримкою Thinking Mode)
|
| 76 |
- **DeepSeek**: DeepSeek Chat
|
| 77 |
|
| 78 |
### 🆕 GPT-5.2 - Нова модель з reasoning!
|
interface.py
CHANGED
|
@@ -800,7 +800,7 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 800 |
label="Модель аналізу",
|
| 801 |
scale=1
|
| 802 |
)
|
| 803 |
-
with gr.Accordion("⚙️ Налаштування аналізу", open=False):
|
| 804 |
with gr.Row():
|
| 805 |
analysis_temp_slider = gr.Slider(
|
| 806 |
minimum=0.0,
|
|
@@ -816,6 +816,38 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 816 |
step=512,
|
| 817 |
label="Max Tokens (ліміт відповіді)"
|
| 818 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
|
| 820 |
question_input = gr.Textbox(
|
| 821 |
label="Уточнююче питання для аналізу",
|
|
@@ -1099,6 +1131,19 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 1099 |
outputs=[batch_thinking_type_dropdown, batch_thinking_level_dropdown, batch_thinking_budget_slider]
|
| 1100 |
)
|
| 1101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1102 |
# generation and analysis
|
| 1103 |
generate_position_button.click(
|
| 1104 |
fn=lambda: (
|
|
@@ -1154,6 +1199,13 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 1154 |
)
|
| 1155 |
|
| 1156 |
analyze_button.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1157 |
fn=analyze_action,
|
| 1158 |
inputs=[
|
| 1159 |
state_lp_json,
|
|
@@ -1162,9 +1214,18 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 1162 |
analysis_provider_dropdown,
|
| 1163 |
analysis_model_dropdown,
|
| 1164 |
analysis_temp_slider,
|
| 1165 |
-
analysis_max_tokens_slider
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1166 |
],
|
| 1167 |
outputs=analysis_output
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1168 |
)
|
| 1169 |
|
| 1170 |
# Settings tab event handlers
|
|
|
|
| 800 |
label="Модель аналізу",
|
| 801 |
scale=1
|
| 802 |
)
|
| 803 |
+
with gr.Accordion("⚙️ Налаштування аналізу", open=False) as analysis_thinking_accordion:
|
| 804 |
with gr.Row():
|
| 805 |
analysis_temp_slider = gr.Slider(
|
| 806 |
minimum=0.0,
|
|
|
|
| 816 |
step=512,
|
| 817 |
label="Max Tokens (ліміт відповіді)"
|
| 818 |
)
|
| 819 |
+
analysis_thinking_enabled_checkbox = gr.Checkbox(
|
| 820 |
+
label="Увімкнути режим Thinking (глибокий аналіз)",
|
| 821 |
+
value=False,
|
| 822 |
+
info="Активує розширений ланцюг міркувань (Gemini 3+, Claude 4.5/4.6)"
|
| 823 |
+
)
|
| 824 |
+
with gr.Row():
|
| 825 |
+
analysis_thinking_type_dropdown = gr.Dropdown(
|
| 826 |
+
choices=["Adaptive", "Enabled"],
|
| 827 |
+
value="Adaptive",
|
| 828 |
+
label="Тип Thinking (Claude)",
|
| 829 |
+
interactive=False
|
| 830 |
+
)
|
| 831 |
+
analysis_thinking_level_dropdown = gr.Dropdown(
|
| 832 |
+
choices=["none", "low", "medium", "high", "xhigh"],
|
| 833 |
+
value="medium",
|
| 834 |
+
label="Рівень Thinking (OpenAI/Gemini)",
|
| 835 |
+
interactive=False
|
| 836 |
+
)
|
| 837 |
+
analysis_openai_verbosity_dropdown = gr.Dropdown(
|
| 838 |
+
choices=["low", "medium", "high"],
|
| 839 |
+
value="medium",
|
| 840 |
+
label="Verbosity (OpenAI GPT-5)",
|
| 841 |
+
interactive=True
|
| 842 |
+
)
|
| 843 |
+
analysis_thinking_budget_slider = gr.Slider(
|
| 844 |
+
minimum=1024,
|
| 845 |
+
maximum=32000,
|
| 846 |
+
value=10000,
|
| 847 |
+
step=1024,
|
| 848 |
+
label="Бюджет токенів (Claude 4.5)",
|
| 849 |
+
interactive=False
|
| 850 |
+
)
|
| 851 |
|
| 852 |
question_input = gr.Textbox(
|
| 853 |
label="Уточнююче питання для аналізу",
|
|
|
|
| 1131 |
outputs=[batch_thinking_type_dropdown, batch_thinking_level_dropdown, batch_thinking_budget_slider]
|
| 1132 |
)
|
| 1133 |
|
| 1134 |
+
# thinking mode settings — Analysis tab
|
| 1135 |
+
analysis_provider_dropdown.change(
|
| 1136 |
+
fn=update_thinking_visibility,
|
| 1137 |
+
inputs=[analysis_provider_dropdown],
|
| 1138 |
+
outputs=[analysis_thinking_accordion]
|
| 1139 |
+
)
|
| 1140 |
+
|
| 1141 |
+
analysis_thinking_enabled_checkbox.change(
|
| 1142 |
+
fn=update_thinking_level_interactive,
|
| 1143 |
+
inputs=[analysis_thinking_enabled_checkbox],
|
| 1144 |
+
outputs=[analysis_thinking_type_dropdown, analysis_thinking_level_dropdown, analysis_thinking_budget_slider]
|
| 1145 |
+
)
|
| 1146 |
+
|
| 1147 |
# generation and analysis
|
| 1148 |
generate_position_button.click(
|
| 1149 |
fn=lambda: (
|
|
|
|
| 1199 |
)
|
| 1200 |
|
| 1201 |
analyze_button.click(
|
| 1202 |
+
fn=lambda: (
|
| 1203 |
+
gr.update(value="⏳ **Аналіз правових позицій...**\n\nЗапит відправлено до AI. Зачекайте, це може зайняти кілька хвилин."),
|
| 1204 |
+
gr.update(interactive=False)
|
| 1205 |
+
),
|
| 1206 |
+
inputs=None,
|
| 1207 |
+
outputs=[analysis_output, analyze_button]
|
| 1208 |
+
).then(
|
| 1209 |
fn=analyze_action,
|
| 1210 |
inputs=[
|
| 1211 |
state_lp_json,
|
|
|
|
| 1214 |
analysis_provider_dropdown,
|
| 1215 |
analysis_model_dropdown,
|
| 1216 |
analysis_temp_slider,
|
| 1217 |
+
analysis_max_tokens_slider,
|
| 1218 |
+
analysis_thinking_enabled_checkbox,
|
| 1219 |
+
analysis_thinking_type_dropdown,
|
| 1220 |
+
analysis_thinking_level_dropdown,
|
| 1221 |
+
analysis_openai_verbosity_dropdown,
|
| 1222 |
+
analysis_thinking_budget_slider
|
| 1223 |
],
|
| 1224 |
outputs=analysis_output
|
| 1225 |
+
).then(
|
| 1226 |
+
fn=lambda: gr.update(interactive=True),
|
| 1227 |
+
inputs=None,
|
| 1228 |
+
outputs=[analyze_button]
|
| 1229 |
)
|
| 1230 |
|
| 1231 |
# Settings tab event handlers
|
main.py
CHANGED
|
@@ -337,7 +337,8 @@ class LLMAnalyzer:
|
|
| 337 |
|
| 338 |
def __init__(self, provider: Any, model_name: Any, temperature: float = GENERATION_TEMPERATURE,
|
| 339 |
max_tokens: Optional[int] = None, thinking_enabled: bool = False,
|
| 340 |
-
thinking_level: str = "medium", openai_verbosity: str = "medium"
|
|
|
|
| 341 |
self.provider = provider
|
| 342 |
self.model_name = model_name
|
| 343 |
self.temperature = temperature
|
|
@@ -345,6 +346,8 @@ class LLMAnalyzer:
|
|
| 345 |
self.thinking_enabled = thinking_enabled
|
| 346 |
self.thinking_level = thinking_level
|
| 347 |
self.openai_verbosity = openai_verbosity
|
|
|
|
|
|
|
| 348 |
|
| 349 |
if provider == ModelProvider.OPENAI:
|
| 350 |
if not OPENAI_API_KEY:
|
|
@@ -513,14 +516,46 @@ class LLMAnalyzer:
|
|
| 513 |
"""Analyze text using Anthropic."""
|
| 514 |
try:
|
| 515 |
_log_prompt("anthropic-analyzer", str(self.model_name), SYSTEM_PROMPT, prompt)
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
|
| 525 |
# Extract JSON from potential markdown blocks
|
| 526 |
json_data = extract_json_from_text(response_text)
|
|
@@ -562,13 +597,20 @@ class LLMAnalyzer:
|
|
| 562 |
),
|
| 563 |
]
|
| 564 |
|
| 565 |
-
|
| 566 |
-
temperature
|
| 567 |
-
max_output_tokens
|
| 568 |
-
system_instruction
|
| 569 |
types.Part.from_text(text=SYSTEM_PROMPT),
|
| 570 |
],
|
| 571 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
|
| 573 |
response = self.client.models.generate_content(
|
| 574 |
model=self.model_name,
|
|
@@ -624,13 +666,16 @@ class PrecedentAnalysisWorkflow(Workflow):
|
|
| 624 |
max_tokens: Optional[int] = None,
|
| 625 |
thinking_enabled: bool = False,
|
| 626 |
thinking_level: str = "medium",
|
| 627 |
-
openai_verbosity: str = "medium"
|
|
|
|
|
|
|
| 628 |
super().__init__()
|
| 629 |
# Use default analysis model if not specified
|
| 630 |
if model_name is None:
|
| 631 |
model_name = DEFAULT_ANALYSIS_MODEL or AnalysisModelName.GPT5_2
|
| 632 |
self.analyzer = LLMAnalyzer(provider, model_name, temperature, max_tokens,
|
| 633 |
-
thinking_enabled, thinking_level, openai_verbosity
|
|
|
|
| 634 |
|
| 635 |
@step
|
| 636 |
async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
|
|
@@ -1345,8 +1390,10 @@ async def analyze_action(
|
|
| 1345 |
temperature: float = GENERATION_TEMPERATURE,
|
| 1346 |
max_tokens: Optional[int] = None,
|
| 1347 |
thinking_enabled: bool = False,
|
|
|
|
| 1348 |
thinking_level: str = "medium",
|
| 1349 |
-
openai_verbosity: str = "medium"
|
|
|
|
| 1350 |
) -> str:
|
| 1351 |
"""Analyze search results using AI."""
|
| 1352 |
try:
|
|
@@ -1356,8 +1403,10 @@ async def analyze_action(
|
|
| 1356 |
temperature=temperature,
|
| 1357 |
max_tokens=max_tokens,
|
| 1358 |
thinking_enabled=thinking_enabled,
|
|
|
|
| 1359 |
thinking_level=thinking_level,
|
| 1360 |
-
openai_verbosity=openai_verbosity
|
|
|
|
| 1361 |
)
|
| 1362 |
|
| 1363 |
query = (
|
|
|
|
| 337 |
|
| 338 |
def __init__(self, provider: Any, model_name: Any, temperature: float = GENERATION_TEMPERATURE,
|
| 339 |
max_tokens: Optional[int] = None, thinking_enabled: bool = False,
|
| 340 |
+
thinking_level: str = "medium", openai_verbosity: str = "medium",
|
| 341 |
+
thinking_type: str = "Adaptive", thinking_budget: int = 10000):
|
| 342 |
self.provider = provider
|
| 343 |
self.model_name = model_name
|
| 344 |
self.temperature = temperature
|
|
|
|
| 346 |
self.thinking_enabled = thinking_enabled
|
| 347 |
self.thinking_level = thinking_level
|
| 348 |
self.openai_verbosity = openai_verbosity
|
| 349 |
+
self.thinking_type = thinking_type
|
| 350 |
+
self.thinking_budget = thinking_budget
|
| 351 |
|
| 352 |
if provider == ModelProvider.OPENAI:
|
| 353 |
if not OPENAI_API_KEY:
|
|
|
|
| 516 |
"""Analyze text using Anthropic."""
|
| 517 |
try:
|
| 518 |
_log_prompt("anthropic-analyzer", str(self.model_name), SYSTEM_PROMPT, prompt)
|
| 519 |
+
|
| 520 |
+
message_params = {
|
| 521 |
+
"model": self.model_name,
|
| 522 |
+
"max_tokens": self.max_tokens or MAX_TOKENS_ANALYSIS,
|
| 523 |
+
"temperature": self.temperature,
|
| 524 |
+
"system": [{"type": "text", "text": SYSTEM_PROMPT, "cache_control": {"type": "ephemeral"}}],
|
| 525 |
+
"messages": [{"role": "user", "content": prompt}]
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
if self.thinking_enabled and "claude" in str(self.model_name).lower():
|
| 529 |
+
if self.thinking_type.lower() == "adaptive" and "-4-6" in str(self.model_name).lower():
|
| 530 |
+
message_params["thinking"] = {"type": "adaptive"}
|
| 531 |
+
message_params["temperature"] = 1.0
|
| 532 |
+
|
| 533 |
+
t_lv = self.thinking_level.lower()
|
| 534 |
+
if t_lv == "xhigh":
|
| 535 |
+
effort = "max"
|
| 536 |
+
elif t_lv in ["low", "medium", "high"]:
|
| 537 |
+
effort = t_lv
|
| 538 |
+
else:
|
| 539 |
+
effort = "medium"
|
| 540 |
+
message_params["output_config"] = {"effort": effort}
|
| 541 |
+
else:
|
| 542 |
+
budget = max(1024, int(self.thinking_budget))
|
| 543 |
+
if message_params["max_tokens"] <= budget:
|
| 544 |
+
message_params["max_tokens"] = budget + 4000
|
| 545 |
+
message_params["thinking"] = {
|
| 546 |
+
"type": "enabled",
|
| 547 |
+
"budget_tokens": budget
|
| 548 |
+
}
|
| 549 |
+
message_params["temperature"] = 1.0
|
| 550 |
+
|
| 551 |
+
response = self.client.messages.create(**message_params)
|
| 552 |
+
|
| 553 |
+
response_text = ""
|
| 554 |
+
for block in response.content:
|
| 555 |
+
if hasattr(block, 'type') and block.type == 'text':
|
| 556 |
+
response_text += getattr(block, 'text', '')
|
| 557 |
+
elif hasattr(block, 'text'):
|
| 558 |
+
response_text += block.text
|
| 559 |
|
| 560 |
# Extract JSON from potential markdown blocks
|
| 561 |
json_data = extract_json_from_text(response_text)
|
|
|
|
| 597 |
),
|
| 598 |
]
|
| 599 |
|
| 600 |
+
config_params = {
|
| 601 |
+
"temperature": self.temperature,
|
| 602 |
+
"max_output_tokens": self.max_tokens or MAX_TOKENS_ANALYSIS,
|
| 603 |
+
"system_instruction": [
|
| 604 |
types.Part.from_text(text=SYSTEM_PROMPT),
|
| 605 |
],
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
if self.thinking_enabled and str(self.model_name).startswith("gemini-3"):
|
| 609 |
+
config_params["thinking_config"] = types.ThinkingConfig(
|
| 610 |
+
thinking_level=self.thinking_level.upper()
|
| 611 |
+
)
|
| 612 |
+
|
| 613 |
+
generate_content_config = types.GenerateContentConfig(**config_params)
|
| 614 |
|
| 615 |
response = self.client.models.generate_content(
|
| 616 |
model=self.model_name,
|
|
|
|
| 666 |
max_tokens: Optional[int] = None,
|
| 667 |
thinking_enabled: bool = False,
|
| 668 |
thinking_level: str = "medium",
|
| 669 |
+
openai_verbosity: str = "medium",
|
| 670 |
+
thinking_type: str = "Adaptive",
|
| 671 |
+
thinking_budget: int = 10000):
|
| 672 |
super().__init__()
|
| 673 |
# Use default analysis model if not specified
|
| 674 |
if model_name is None:
|
| 675 |
model_name = DEFAULT_ANALYSIS_MODEL or AnalysisModelName.GPT5_2
|
| 676 |
self.analyzer = LLMAnalyzer(provider, model_name, temperature, max_tokens,
|
| 677 |
+
thinking_enabled, thinking_level, openai_verbosity,
|
| 678 |
+
thinking_type, thinking_budget)
|
| 679 |
|
| 680 |
@step
|
| 681 |
async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
|
|
|
|
| 1390 |
temperature: float = GENERATION_TEMPERATURE,
|
| 1391 |
max_tokens: Optional[int] = None,
|
| 1392 |
thinking_enabled: bool = False,
|
| 1393 |
+
thinking_type: str = "Adaptive",
|
| 1394 |
thinking_level: str = "medium",
|
| 1395 |
+
openai_verbosity: str = "medium",
|
| 1396 |
+
thinking_budget: int = 10000
|
| 1397 |
) -> str:
|
| 1398 |
"""Analyze search results using AI."""
|
| 1399 |
try:
|
|
|
|
| 1403 |
temperature=temperature,
|
| 1404 |
max_tokens=max_tokens,
|
| 1405 |
thinking_enabled=thinking_enabled,
|
| 1406 |
+
thinking_type=thinking_type,
|
| 1407 |
thinking_level=thinking_level,
|
| 1408 |
+
openai_verbosity=openai_verbosity,
|
| 1409 |
+
thinking_budget=thinking_budget
|
| 1410 |
)
|
| 1411 |
|
| 1412 |
query = (
|