feat: Оновлення UI для GPT-5.2 та додавання контролю MaxTokens
Browse files- HELP.md +29 -18
- config/environments/default.yaml +19 -15
- config/models.py +6 -7
- interface.py +75 -17
- main.py +47 -28
- prompts.py +20 -10
- test_anthropic_rest.py +51 -0
HELP.md
CHANGED
|
@@ -40,14 +40,20 @@
|
|
| 40 |
- **Модель генерації**: Виберіть конкретну модель (наприклад, GPT-4o, Claude 3.5 Sonnet, Gemini 3.0 Flash)
|
| 41 |
|
| 42 |
**Рекомендації:**
|
| 43 |
-
- Для швидкої роботи: Gemini 3.0 Flash, GPT-4o-mini
|
| 44 |
-
- Для якісних результатів: Claude
|
| 45 |
- Для економії: DeepSeek Chat
|
| 46 |
|
| 47 |
-
#### 2.
|
| 48 |
-
-
|
| 49 |
-
-
|
| 50 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
#### 3. Спосіб вводу даних
|
| 53 |
|
|
@@ -134,10 +140,11 @@
|
|
| 134 |
#### 1. Попередні кроки
|
| 135 |
⚠️ Спочатку потрібно виконати пошук у закладці "Пошук"
|
| 136 |
|
| 137 |
-
#### 2. Вибір моделі аналізу
|
| 138 |
- Оберіть провайдер та модель для аналізу
|
| 139 |
-
-
|
| 140 |
-
-
|
|
|
|
| 141 |
|
| 142 |
#### 3. Уточнююче питання (опціонально)
|
| 143 |
Додайте конкретне питання для AI, наприклад:
|
|
@@ -334,28 +341,32 @@ df['text'] = df['parsed'].apply(lambda x: x['text'])
|
|
| 334 |
### OpenAI
|
| 335 |
|
| 336 |
**Моделі:**
|
| 337 |
-
- GPT-
|
|
|
|
|
|
|
| 338 |
- GPT-4o-mini - баланс ціна/якість
|
| 339 |
-
- GPT-4.1 -
|
| 340 |
- Fine-tuned моделі - власні налаштовані моделі
|
| 341 |
|
| 342 |
**Особливості:**
|
| 343 |
- Швидка обробка
|
| 344 |
-
-
|
| 345 |
-
-
|
| 346 |
|
| 347 |
**API Key:** `OPENAI_API_KEY`
|
| 348 |
|
| 349 |
### Anthropic (Claude)
|
| 350 |
|
| 351 |
**Моделі:**
|
| 352 |
-
- Claude
|
| 353 |
-
- Claude 4.
|
|
|
|
| 354 |
|
| 355 |
**Особливості:**
|
| 356 |
-
- Детальний аналіз
|
| 357 |
-
-
|
| 358 |
-
-
|
|
|
|
| 359 |
|
| 360 |
**API Key:** `ANTHROPIC_API_KEY`
|
| 361 |
|
|
|
|
| 40 |
- **Модель генерації**: Виберіть конкретну модель (наприклад, GPT-4o, Claude 3.5 Sonnet, Gemini 3.0 Flash)
|
| 41 |
|
| 42 |
**Рекомендації:**
|
| 43 |
+
- Для швидкої роботи: Gemini 3.0 Flash, GPT-5 Mini, GPT-4o-mini
|
| 44 |
+
- Для якісних результатів: GPT-5.2, Claude Sonnet 4.6, GPT-4o
|
| 45 |
- Для економії: DeepSeek Chat
|
| 46 |
|
| 47 |
+
#### 2. Додаткові параметри (Температура, Thinking Mode, Verbosity)
|
| 48 |
+
- **Температура генерації**: дозволяє налаштовувати креативність моделі від 0.0 (строга) до 2.0 (креативна).
|
| 49 |
+
- **Режим Thinking (для OpenAI GPT-5+, Gemini 3+ та Claude 4.5/4.6)**:
|
| 50 |
+
- Увімкніть для глибшого аналізу складних рішень.
|
| 51 |
+
- **Тип Thinking (Claude)**:
|
| 52 |
+
- **Adaptive**: модель сама обирає глибину міркувань (працює для моделей 4.6).
|
| 53 |
+
- **Enabled**: класичний режим із жорстким бюджетом токенів (для моделей 4.5 та 4.6).
|
| 54 |
+
- **Рівні Thinking (OpenAI / Gemini)**: none → low → medium → high → xhigh (у GPT-5.2)
|
| 55 |
+
- **Verbosity (OpenAI GPT-5)**: керування багатослівністю (low, medium, high)
|
| 56 |
+
- **Бюджет токенів (Claude 4.5 або Enabled)**: 1024-32000
|
| 57 |
|
| 58 |
#### 3. Спосіб вводу даних
|
| 59 |
|
|
|
|
| 140 |
#### 1. Попередні кроки
|
| 141 |
⚠️ Спочатку потрібно виконати пошук у закладці "Пошук"
|
| 142 |
|
| 143 |
+
#### 2. Вибір моделі аналізу та налаштувань
|
| 144 |
- Оберіть провайдер та модель для аналізу
|
| 145 |
+
- Рекомендовано: GPT-5.2, Claude Sonnet 4.6
|
| 146 |
+
- **Температура аналізу**: налаштування креативності порівняння.
|
| 147 |
+
- **Max Tokens (ліміт відповіді)**: максимальний обсяг деталізації аналізу (від 512 до 32768).
|
| 148 |
|
| 149 |
#### 3. Уточнююче питання (опціонально)
|
| 150 |
Додайте конкретне питання для AI, наприклад:
|
|
|
|
| 341 |
### OpenAI
|
| 342 |
|
| 343 |
**Моделі:**
|
| 344 |
+
- GPT-5.2 - остання потужна модель (рекомендована)
|
| 345 |
+
- GPT-5 Mini - швидка і потужна модель нового покоління
|
| 346 |
+
- GPT-4o - попередня потужна модель
|
| 347 |
- GPT-4o-mini - баланс ціна/якість
|
| 348 |
+
- GPT-4.1 - версія GPT-4 (reasoning)
|
| 349 |
- Fine-tuned моделі - власні налаштовані моделі
|
| 350 |
|
| 351 |
**Особливості:**
|
| 352 |
- Швидка обробка
|
| 353 |
+
- Підтримка Reasoning Effort (до `xhigh` в GPT-5.2) та Verbosity
|
| 354 |
+
- Висока якість структурованих даних (JSON Schema)
|
| 355 |
|
| 356 |
**API Key:** `OPENAI_API_KEY`
|
| 357 |
|
| 358 |
### Anthropic (Claude)
|
| 359 |
|
| 360 |
**Моделі:**
|
| 361 |
+
- Claude Sonnet 4.6 - рекомендована (за замовчуванням)
|
| 362 |
+
- Claude Opus 4.6 - найпотужніша
|
| 363 |
+
- Claude Haiku 4.5 - швидка модель
|
| 364 |
|
| 365 |
**Особливості:**
|
| 366 |
+
- Детальний аналіз та структурування
|
| 367 |
+
- **Adaptive Thinking** у серії 4.6 для гнучкого використання ресурсів
|
| 368 |
+
- **Extended Thinking** у серії 4.5 для складних завдань
|
| 369 |
+
- Великий контекст
|
| 370 |
|
| 371 |
**API Key:** `ANTHROPIC_API_KEY`
|
| 372 |
|
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 |
|
|
@@ -46,6 +46,9 @@ models:
|
|
| 46 |
openai:
|
| 47 |
- name: "gpt-5.2"
|
| 48 |
display_name: "GPT-5.2"
|
|
|
|
|
|
|
|
|
|
| 49 |
- name: "gpt-4.1"
|
| 50 |
display_name: "GPT-4.1"
|
| 51 |
- name: "ft:gpt-4o-mini-2024-07-18:personal:lp-1700-part-cd-120:AqhCe5Aq"
|
|
@@ -54,13 +57,13 @@ models:
|
|
| 54 |
display_name: "GPT-4o Mini FT2"
|
| 55 |
|
| 56 |
anthropic:
|
| 57 |
-
- name: "claude-opus-4-
|
| 58 |
-
display_name: "Claude Opus 4.
|
|
|
|
|
|
|
|
|
|
| 59 |
- name: "claude-haiku-4-5-20251001"
|
| 60 |
display_name: "Claude Haiku 4.5"
|
| 61 |
-
- name: "claude-sonnet-4-5-20250929"
|
| 62 |
-
display_name: "Claude Sonnet 4.5"
|
| 63 |
-
default: true
|
| 64 |
|
| 65 |
gemini:
|
| 66 |
- name: "gemini-3-flash-preview"
|
|
@@ -77,6 +80,8 @@ models:
|
|
| 77 |
openai:
|
| 78 |
- name: "gpt-5.2"
|
| 79 |
display_name: "GPT-5.2"
|
|
|
|
|
|
|
| 80 |
- name: "gpt-4.1"
|
| 81 |
display_name: "GPT-4.1"
|
| 82 |
- name: "gpt-4o"
|
|
@@ -85,14 +90,13 @@ models:
|
|
| 85 |
display_name: "GPT-4o Mini"
|
| 86 |
|
| 87 |
anthropic:
|
| 88 |
-
- name: "claude-
|
| 89 |
-
display_name: "Claude
|
| 90 |
-
- name: "claude-
|
| 91 |
-
display_name: "Claude
|
|
|
|
| 92 |
- name: "claude-haiku-4-5-20251001"
|
| 93 |
display_name: "Claude Haiku 4.5"
|
| 94 |
-
- name: "claude-sonnet-4-5-20250929"
|
| 95 |
-
display_name: "Claude Sonnet 4.5"
|
| 96 |
|
| 97 |
gemini:
|
| 98 |
- name: "gemini-3-flash-preview"
|
|
|
|
| 23 |
# Generation Settings
|
| 24 |
generation:
|
| 25 |
max_tokens:
|
| 26 |
+
openai: 8192
|
| 27 |
+
anthropic: 16000
|
| 28 |
+
gemini: 8192
|
| 29 |
+
deepseek: 8192
|
| 30 |
max_tokens_analysis: 4000
|
| 31 |
temperature: 0.5
|
| 32 |
|
|
|
|
| 46 |
openai:
|
| 47 |
- name: "gpt-5.2"
|
| 48 |
display_name: "GPT-5.2"
|
| 49 |
+
default: true
|
| 50 |
+
- name: "gpt-5-mini"
|
| 51 |
+
display_name: "GPT-5 Mini"
|
| 52 |
- name: "gpt-4.1"
|
| 53 |
display_name: "GPT-4.1"
|
| 54 |
- name: "ft:gpt-4o-mini-2024-07-18:personal:lp-1700-part-cd-120:AqhCe5Aq"
|
|
|
|
| 57 |
display_name: "GPT-4o Mini FT2"
|
| 58 |
|
| 59 |
anthropic:
|
| 60 |
+
- name: "claude-opus-4-6"
|
| 61 |
+
display_name: "Claude Opus 4.6"
|
| 62 |
+
- name: "claude-sonnet-4-6"
|
| 63 |
+
display_name: "Claude Sonnet 4.6"
|
| 64 |
+
default: true
|
| 65 |
- name: "claude-haiku-4-5-20251001"
|
| 66 |
display_name: "Claude Haiku 4.5"
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
gemini:
|
| 69 |
- name: "gemini-3-flash-preview"
|
|
|
|
| 80 |
openai:
|
| 81 |
- name: "gpt-5.2"
|
| 82 |
display_name: "GPT-5.2"
|
| 83 |
+
- name: "gpt-5-mini"
|
| 84 |
+
display_name: "GPT-5 Mini"
|
| 85 |
- name: "gpt-4.1"
|
| 86 |
display_name: "GPT-4.1"
|
| 87 |
- name: "gpt-4o"
|
|
|
|
| 90 |
display_name: "GPT-4o Mini"
|
| 91 |
|
| 92 |
anthropic:
|
| 93 |
+
- name: "claude-opus-4-6"
|
| 94 |
+
display_name: "Claude Opus 4.6"
|
| 95 |
+
- name: "claude-sonnet-4-6"
|
| 96 |
+
display_name: "Claude Sonnet 4.6"
|
| 97 |
+
default: true
|
| 98 |
- name: "claude-haiku-4-5-20251001"
|
| 99 |
display_name: "Claude Haiku 4.5"
|
|
|
|
|
|
|
| 100 |
|
| 101 |
gemini:
|
| 102 |
- name: "gemini-3-flash-preview"
|
config/models.py
CHANGED
|
@@ -69,23 +69,22 @@ class ModelRegistry:
|
|
| 69 |
# Generic fine-tuned model
|
| 70 |
return 'GPT4o_FT'
|
| 71 |
|
| 72 |
-
# Handle specific models
|
| 73 |
if model_name == 'gpt-5.2':
|
| 74 |
return 'GPT5_2'
|
|
|
|
|
|
|
| 75 |
elif model_name == 'gpt-4.1':
|
| 76 |
return 'GPT4_1'
|
| 77 |
elif model_name == 'gpt-4o':
|
| 78 |
return 'GPT4o'
|
| 79 |
elif model_name == 'gpt-4o-mini':
|
| 80 |
return 'GPT4o_MINI'
|
| 81 |
-
elif model_name == 'claude-
|
| 82 |
-
return '
|
| 83 |
-
elif model_name == 'claude-
|
| 84 |
-
return '
|
| 85 |
elif model_name == 'claude-haiku-4-5-20251001':
|
| 86 |
return 'CLAUDE_HAIKU_4_5'
|
| 87 |
-
elif model_name == 'claude-sonnet-4-5-20250929':
|
| 88 |
-
return 'CLAUDE_SONNET_4_5'
|
| 89 |
elif model_name == 'gemini-3-flash-preview':
|
| 90 |
return 'GEMINI_3_FLASH'
|
| 91 |
elif model_name == 'gemini-3-pro-preview':
|
|
|
|
| 69 |
# Generic fine-tuned model
|
| 70 |
return 'GPT4o_FT'
|
| 71 |
|
|
|
|
| 72 |
if model_name == 'gpt-5.2':
|
| 73 |
return 'GPT5_2'
|
| 74 |
+
elif model_name == 'gpt-5-mini':
|
| 75 |
+
return 'GPT5_MINI'
|
| 76 |
elif model_name == 'gpt-4.1':
|
| 77 |
return 'GPT4_1'
|
| 78 |
elif model_name == 'gpt-4o':
|
| 79 |
return 'GPT4o'
|
| 80 |
elif model_name == 'gpt-4o-mini':
|
| 81 |
return 'GPT4o_MINI'
|
| 82 |
+
elif model_name == 'claude-opus-4-6':
|
| 83 |
+
return 'CLAUDE_OPUS_4_6'
|
| 84 |
+
elif model_name == 'claude-sonnet-4-6':
|
| 85 |
+
return 'CLAUDE_SONNET_4_6'
|
| 86 |
elif model_name == 'claude-haiku-4-5-20251001':
|
| 87 |
return 'CLAUDE_HAIKU_4_5'
|
|
|
|
|
|
|
| 88 |
elif model_name == 'gemini-3-flash-preview':
|
| 89 |
return 'GEMINI_3_FLASH'
|
| 90 |
elif model_name == 'gemini-3-pro-preview':
|
interface.py
CHANGED
|
@@ -44,7 +44,7 @@ def update_generation_model_choices(provider: str) -> gr.Dropdown:
|
|
| 44 |
if provider == ModelProvider.OPENAI.value:
|
| 45 |
return gr.Dropdown(
|
| 46 |
choices=[m.value for m in GenerationModelName if m.value.startswith("ft:") or m.value.startswith("gpt")],
|
| 47 |
-
value=GenerationModelName.
|
| 48 |
label="Модель генерації"
|
| 49 |
)
|
| 50 |
if provider == ModelProvider.DEEPSEEK.value:
|
|
@@ -56,7 +56,7 @@ def update_generation_model_choices(provider: str) -> gr.Dropdown:
|
|
| 56 |
elif provider == ModelProvider.ANTHROPIC.value:
|
| 57 |
return gr.Dropdown(
|
| 58 |
choices=[m.value for m in GenerationModelName if m.value.startswith("claude")],
|
| 59 |
-
value=GenerationModelName.
|
| 60 |
label="Модель генерації"
|
| 61 |
)
|
| 62 |
else: # GEMINI
|
|
@@ -66,13 +66,14 @@ def update_generation_model_choices(provider: str) -> gr.Dropdown:
|
|
| 66 |
label="Модель генерації"
|
| 67 |
)
|
| 68 |
|
| 69 |
-
def update_thinking_visibility(provider: str):
|
| 70 |
"""Show/hide thinking controls based on provider."""
|
| 71 |
-
return gr.update(visible=(provider in [ModelProvider.GEMINI.value, ModelProvider.ANTHROPIC.value]))
|
| 72 |
|
| 73 |
def update_thinking_level_interactive(thinking_enabled: bool) -> tuple:
|
| 74 |
"""Enable/disable thinking controls based on checkbox."""
|
| 75 |
return (
|
|
|
|
| 76 |
gr.Dropdown(interactive=thinking_enabled),
|
| 77 |
gr.Slider(interactive=thinking_enabled)
|
| 78 |
)
|
|
@@ -153,7 +154,7 @@ def update_analysis_model_choices(provider: str) -> gr.Dropdown:
|
|
| 153 |
if provider == ModelProvider.OPENAI.value:
|
| 154 |
return gr.Dropdown(
|
| 155 |
choices=[m.value for m in AnalysisModelName if m.value.startswith("gpt")],
|
| 156 |
-
value=AnalysisModelName.
|
| 157 |
label="Модель аналізу"
|
| 158 |
)
|
| 159 |
elif provider == ModelProvider.DEEPSEEK.value:
|
|
@@ -165,7 +166,7 @@ def update_analysis_model_choices(provider: str) -> gr.Dropdown:
|
|
| 165 |
elif provider == ModelProvider.ANTHROPIC.value:
|
| 166 |
return gr.Dropdown(
|
| 167 |
choices=[m.value for m in AnalysisModelName if m.value.startswith("claude")],
|
| 168 |
-
value=AnalysisModelName.
|
| 169 |
label="Модель аналізу"
|
| 170 |
)
|
| 171 |
else: # GEMINI
|
|
@@ -185,8 +186,12 @@ async def process_input(
|
|
| 185 |
provider: str,
|
| 186 |
model_name: str,
|
| 187 |
thinking_enabled: bool = False,
|
|
|
|
| 188 |
thinking_level: str = "MEDIUM",
|
|
|
|
| 189 |
thinking_budget: int = 10000,
|
|
|
|
|
|
|
| 190 |
session_id: str = None
|
| 191 |
) -> Tuple[str, Optional[Dict[str, Any]], str]:
|
| 192 |
"""Process input and generate legal position."""
|
|
@@ -242,8 +247,12 @@ async def process_input(
|
|
| 242 |
provider,
|
| 243 |
model_name,
|
| 244 |
thinking_enabled,
|
|
|
|
| 245 |
thinking_level,
|
|
|
|
| 246 |
thinking_budget,
|
|
|
|
|
|
|
| 247 |
custom_system_prompt,
|
| 248 |
custom_lp_prompt
|
| 249 |
)
|
|
@@ -609,25 +618,52 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 609 |
)
|
| 610 |
|
| 611 |
# Advanced Settings in Accordion to save space
|
| 612 |
-
with gr.Accordion("⚙️ Додаткові параметри
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 613 |
thinking_enabled_checkbox = gr.Checkbox(
|
| 614 |
label="Увімкнути режим Thinking (глибокий аналіз)",
|
| 615 |
value=False,
|
| 616 |
-
info="Активує розширений ланцюг міркувань
|
| 617 |
)
|
| 618 |
with gr.Row():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
thinking_level_dropdown = gr.Dropdown(
|
| 620 |
-
choices=["
|
| 621 |
-
value="
|
| 622 |
-
label="Рівень Thinking (Gemini)",
|
| 623 |
interactive=False
|
| 624 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
thinking_budget_slider = gr.Slider(
|
| 626 |
-
minimum=
|
| 627 |
-
maximum=
|
| 628 |
value=10000,
|
| 629 |
-
step=
|
| 630 |
-
label="Бюджет токенів (Claude)",
|
| 631 |
interactive=False
|
| 632 |
)
|
| 633 |
|
|
@@ -722,6 +758,22 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 722 |
label="Модель аналізу",
|
| 723 |
scale=1
|
| 724 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 725 |
|
| 726 |
question_input = gr.Textbox(
|
| 727 |
label="Уточнююче питання для аналізу",
|
|
@@ -930,7 +982,7 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 930 |
thinking_enabled_checkbox.change(
|
| 931 |
fn=update_thinking_level_interactive,
|
| 932 |
inputs=[thinking_enabled_checkbox],
|
| 933 |
-
outputs=[thinking_level_dropdown, thinking_budget_slider]
|
| 934 |
)
|
| 935 |
|
| 936 |
# generation and analysis
|
|
@@ -945,8 +997,12 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 945 |
generation_provider_dropdown,
|
| 946 |
generation_model_dropdown,
|
| 947 |
thinking_enabled_checkbox,
|
|
|
|
| 948 |
thinking_level_dropdown,
|
|
|
|
| 949 |
thinking_budget_slider,
|
|
|
|
|
|
|
| 950 |
session_id_state
|
| 951 |
],
|
| 952 |
outputs=[position_output, state_lp_json, session_id_state]
|
|
@@ -983,7 +1039,9 @@ def create_gradio_interface() -> gr.Blocks:
|
|
| 983 |
question_input,
|
| 984 |
state_nodes,
|
| 985 |
analysis_provider_dropdown,
|
| 986 |
-
analysis_model_dropdown
|
|
|
|
|
|
|
| 987 |
],
|
| 988 |
outputs=analysis_output
|
| 989 |
)
|
|
|
|
| 44 |
if provider == ModelProvider.OPENAI.value:
|
| 45 |
return gr.Dropdown(
|
| 46 |
choices=[m.value for m in GenerationModelName if m.value.startswith("ft:") or m.value.startswith("gpt")],
|
| 47 |
+
value=GenerationModelName.GPT5_2.value,
|
| 48 |
label="Модель генерації"
|
| 49 |
)
|
| 50 |
if provider == ModelProvider.DEEPSEEK.value:
|
|
|
|
| 56 |
elif provider == ModelProvider.ANTHROPIC.value:
|
| 57 |
return gr.Dropdown(
|
| 58 |
choices=[m.value for m in GenerationModelName if m.value.startswith("claude")],
|
| 59 |
+
value=GenerationModelName.CLAUDE_SONNET_4_6.value,
|
| 60 |
label="Модель генерації"
|
| 61 |
)
|
| 62 |
else: # GEMINI
|
|
|
|
| 66 |
label="Модель генерації"
|
| 67 |
)
|
| 68 |
|
| 69 |
+
def update_thinking_visibility(provider: str) -> gr.update:
|
| 70 |
"""Show/hide thinking controls based on provider."""
|
| 71 |
+
return gr.update(visible=(provider in [ModelProvider.GEMINI.value, ModelProvider.ANTHROPIC.value, ModelProvider.OPENAI.value]))
|
| 72 |
|
| 73 |
def update_thinking_level_interactive(thinking_enabled: bool) -> tuple:
|
| 74 |
"""Enable/disable thinking controls based on checkbox."""
|
| 75 |
return (
|
| 76 |
+
gr.Dropdown(interactive=thinking_enabled),
|
| 77 |
gr.Dropdown(interactive=thinking_enabled),
|
| 78 |
gr.Slider(interactive=thinking_enabled)
|
| 79 |
)
|
|
|
|
| 154 |
if provider == ModelProvider.OPENAI.value:
|
| 155 |
return gr.Dropdown(
|
| 156 |
choices=[m.value for m in AnalysisModelName if m.value.startswith("gpt")],
|
| 157 |
+
value=AnalysisModelName.GPT5_2.value,
|
| 158 |
label="Модель аналізу"
|
| 159 |
)
|
| 160 |
elif provider == ModelProvider.DEEPSEEK.value:
|
|
|
|
| 166 |
elif provider == ModelProvider.ANTHROPIC.value:
|
| 167 |
return gr.Dropdown(
|
| 168 |
choices=[m.value for m in AnalysisModelName if m.value.startswith("claude")],
|
| 169 |
+
value=AnalysisModelName.CLAUDE_SONNET_4_6.value,
|
| 170 |
label="Модель аналізу"
|
| 171 |
)
|
| 172 |
else: # GEMINI
|
|
|
|
| 186 |
provider: str,
|
| 187 |
model_name: str,
|
| 188 |
thinking_enabled: bool = False,
|
| 189 |
+
thinking_type: str = "Adaptive",
|
| 190 |
thinking_level: str = "MEDIUM",
|
| 191 |
+
openai_verbosity: str = "medium",
|
| 192 |
thinking_budget: int = 10000,
|
| 193 |
+
temperature: float = 0.5,
|
| 194 |
+
max_tokens: int = 4000,
|
| 195 |
session_id: str = None
|
| 196 |
) -> Tuple[str, Optional[Dict[str, Any]], str]:
|
| 197 |
"""Process input and generate legal position."""
|
|
|
|
| 247 |
provider,
|
| 248 |
model_name,
|
| 249 |
thinking_enabled,
|
| 250 |
+
thinking_type,
|
| 251 |
thinking_level,
|
| 252 |
+
openai_verbosity,
|
| 253 |
thinking_budget,
|
| 254 |
+
temperature,
|
| 255 |
+
max_tokens,
|
| 256 |
custom_system_prompt,
|
| 257 |
custom_lp_prompt
|
| 258 |
)
|
|
|
|
| 618 |
)
|
| 619 |
|
| 620 |
# Advanced Settings in Accordion to save space
|
| 621 |
+
with gr.Accordion("⚙️ Додаткові параметри", open=False) as thinking_accordion:
|
| 622 |
+
with gr.Row():
|
| 623 |
+
generation_temp_slider = gr.Slider(
|
| 624 |
+
minimum=0.0,
|
| 625 |
+
maximum=2.0,
|
| 626 |
+
value=0.5,
|
| 627 |
+
step=0.1,
|
| 628 |
+
label="Температура генерації (креативність)"
|
| 629 |
+
)
|
| 630 |
+
generation_max_tokens_slider = gr.Slider(
|
| 631 |
+
minimum=512,
|
| 632 |
+
maximum=32768,
|
| 633 |
+
value=4000,
|
| 634 |
+
step=512,
|
| 635 |
+
label="Max Tokens (ліміт відповіді)"
|
| 636 |
+
)
|
| 637 |
thinking_enabled_checkbox = gr.Checkbox(
|
| 638 |
label="Увімкнути режим Thinking (глибокий аналіз)",
|
| 639 |
value=False,
|
| 640 |
+
info="Активує розширений ланцюг міркувань (Gemini 3+, Claude 4.5/4.6)"
|
| 641 |
)
|
| 642 |
with gr.Row():
|
| 643 |
+
thinking_type_dropdown = gr.Dropdown(
|
| 644 |
+
choices=["Adaptive", "Enabled"],
|
| 645 |
+
value="Adaptive",
|
| 646 |
+
label="Тип Thinking (Claude)",
|
| 647 |
+
interactive=False
|
| 648 |
+
)
|
| 649 |
thinking_level_dropdown = gr.Dropdown(
|
| 650 |
+
choices=["none", "low", "medium", "high", "xhigh"],
|
| 651 |
+
value="medium",
|
| 652 |
+
label="Рівень Thinking (OpenAI/Gemini)",
|
| 653 |
interactive=False
|
| 654 |
)
|
| 655 |
+
openai_verbosity_dropdown = gr.Dropdown(
|
| 656 |
+
choices=["low", "medium", "high"],
|
| 657 |
+
value="medium",
|
| 658 |
+
label="Verbosity (OpenAI GPT-5)",
|
| 659 |
+
interactive=True
|
| 660 |
+
)
|
| 661 |
thinking_budget_slider = gr.Slider(
|
| 662 |
+
minimum=1024,
|
| 663 |
+
maximum=32000,
|
| 664 |
value=10000,
|
| 665 |
+
step=1024,
|
| 666 |
+
label="Бюджет токенів (Claude 4.5)",
|
| 667 |
interactive=False
|
| 668 |
)
|
| 669 |
|
|
|
|
| 758 |
label="Модель аналізу",
|
| 759 |
scale=1
|
| 760 |
)
|
| 761 |
+
with gr.Accordion("⚙️ Налаштування аналізу", open=False):
|
| 762 |
+
with gr.Row():
|
| 763 |
+
analysis_temp_slider = gr.Slider(
|
| 764 |
+
minimum=0.0,
|
| 765 |
+
maximum=2.0,
|
| 766 |
+
value=0.5,
|
| 767 |
+
step=0.1,
|
| 768 |
+
label="Температура аналізу"
|
| 769 |
+
)
|
| 770 |
+
analysis_max_tokens_slider = gr.Slider(
|
| 771 |
+
minimum=512,
|
| 772 |
+
maximum=32768,
|
| 773 |
+
value=4000,
|
| 774 |
+
step=512,
|
| 775 |
+
label="Max Tokens (ліміт відповіді)"
|
| 776 |
+
)
|
| 777 |
|
| 778 |
question_input = gr.Textbox(
|
| 779 |
label="Уточнююче питання для аналізу",
|
|
|
|
| 982 |
thinking_enabled_checkbox.change(
|
| 983 |
fn=update_thinking_level_interactive,
|
| 984 |
inputs=[thinking_enabled_checkbox],
|
| 985 |
+
outputs=[thinking_type_dropdown, thinking_level_dropdown, thinking_budget_slider]
|
| 986 |
)
|
| 987 |
|
| 988 |
# generation and analysis
|
|
|
|
| 997 |
generation_provider_dropdown,
|
| 998 |
generation_model_dropdown,
|
| 999 |
thinking_enabled_checkbox,
|
| 1000 |
+
thinking_type_dropdown,
|
| 1001 |
thinking_level_dropdown,
|
| 1002 |
+
openai_verbosity_dropdown,
|
| 1003 |
thinking_budget_slider,
|
| 1004 |
+
generation_temp_slider,
|
| 1005 |
+
generation_max_tokens_slider,
|
| 1006 |
session_id_state
|
| 1007 |
],
|
| 1008 |
outputs=[position_output, state_lp_json, session_id_state]
|
|
|
|
| 1039 |
question_input,
|
| 1040 |
state_nodes,
|
| 1041 |
analysis_provider_dropdown,
|
| 1042 |
+
analysis_model_dropdown,
|
| 1043 |
+
analysis_temp_slider,
|
| 1044 |
+
analysis_max_tokens_slider
|
| 1045 |
],
|
| 1046 |
outputs=analysis_output
|
| 1047 |
)
|
main.py
CHANGED
|
@@ -272,9 +272,11 @@ class RetrieverEvent(Event):
|
|
| 272 |
class LLMAnalyzer:
|
| 273 |
"""Class for handling different LLM providers."""
|
| 274 |
|
| 275 |
-
def __init__(self, provider: Any, model_name: Any):
|
| 276 |
self.provider = provider
|
| 277 |
self.model_name = model_name
|
|
|
|
|
|
|
| 278 |
|
| 279 |
if provider == ModelProvider.OPENAI:
|
| 280 |
if not OPENAI_API_KEY:
|
|
@@ -342,7 +344,7 @@ class LLMAnalyzer:
|
|
| 342 |
|
| 343 |
# Reasoning models usually require temperature=1.0 or none
|
| 344 |
if not is_reasoning_model:
|
| 345 |
-
completion_params["temperature"] =
|
| 346 |
|
| 347 |
# Add GPT-5.2 specific parameters
|
| 348 |
if "gpt-5" in model_val.lower():
|
|
@@ -397,7 +399,7 @@ class LLMAnalyzer:
|
|
| 397 |
# Use JSON mode and temperature only for non-reasoning models
|
| 398 |
if not is_reasoning:
|
| 399 |
completion_params["response_format"] = {'type': 'json_object'}
|
| 400 |
-
completion_params["temperature"] =
|
| 401 |
|
| 402 |
# Retry logic for DeepSeek analysis
|
| 403 |
max_retries = 3
|
|
@@ -429,7 +431,8 @@ class LLMAnalyzer:
|
|
| 429 |
try:
|
| 430 |
response = self.client.messages.create(
|
| 431 |
model=self.model_name,
|
| 432 |
-
max_tokens=MAX_TOKENS_ANALYSIS,
|
|
|
|
| 433 |
system=SYSTEM_PROMPT,
|
| 434 |
messages=[{"role": "user", "content": prompt}]
|
| 435 |
)
|
|
@@ -473,8 +476,8 @@ class LLMAnalyzer:
|
|
| 473 |
]
|
| 474 |
|
| 475 |
generate_content_config = types.GenerateContentConfig(
|
| 476 |
-
temperature=
|
| 477 |
-
max_output_tokens=MAX_TOKENS_ANALYSIS,
|
| 478 |
system_instruction=[
|
| 479 |
types.Part.from_text(text=SYSTEM_PROMPT),
|
| 480 |
],
|
|
@@ -529,9 +532,11 @@ class PrecedentAnalysisWorkflow(Workflow):
|
|
| 529 |
"""Workflow for analyzing legal precedents."""
|
| 530 |
|
| 531 |
def __init__(self, provider: Any = ModelProvider.OPENAI,
|
| 532 |
-
model_name: Any = AnalysisModelName.GPT4o_MINI
|
|
|
|
|
|
|
| 533 |
super().__init__()
|
| 534 |
-
self.analyzer = LLMAnalyzer(provider, model_name)
|
| 535 |
|
| 536 |
@step
|
| 537 |
async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
|
|
@@ -612,8 +617,12 @@ def generate_legal_position(
|
|
| 612 |
provider: str,
|
| 613 |
model_name: str,
|
| 614 |
thinking_enabled: bool = False,
|
|
|
|
| 615 |
thinking_level: str = "MEDIUM",
|
|
|
|
| 616 |
thinking_budget: int = 10000,
|
|
|
|
|
|
|
| 617 |
custom_system_prompt: Optional[str] = None,
|
| 618 |
custom_lp_prompt: Optional[str] = None
|
| 619 |
) -> Dict:
|
|
@@ -746,17 +755,17 @@ def generate_legal_position(
|
|
| 746 |
|
| 747 |
# Set tokens based on model capabilities
|
| 748 |
if is_reasoning_model:
|
| 749 |
-
completion_params["max_completion_tokens"] = MAX_TOKENS_CONFIG["openai"]
|
| 750 |
else:
|
| 751 |
-
completion_params["max_tokens"] = MAX_TOKENS_CONFIG["openai"]
|
| 752 |
-
completion_params["temperature"] =
|
| 753 |
|
| 754 |
# Handle thinking/reasoning for GPT-5.2 and other reasoning models
|
| 755 |
if thinking_enabled and is_reasoning_model:
|
| 756 |
# GPT-5.2 specific parameters
|
| 757 |
if "gpt-5" in model_name.lower():
|
| 758 |
completion_params["reasoning_effort"] = thinking_level.lower()
|
| 759 |
-
completion_params["verbosity"] =
|
| 760 |
completion_params["store"] = False
|
| 761 |
else:
|
| 762 |
# For other reasoning models (gpt-4.1, o1, etc.)
|
|
@@ -848,16 +857,15 @@ def generate_legal_position(
|
|
| 848 |
messages.append({"role": "user", "content": combined_content})
|
| 849 |
else:
|
| 850 |
messages.append({"role": "system", "content": system_prompt})
|
| 851 |
-
messages.append({"role": "user", "content": content})
|
| 852 |
-
|
| 853 |
completion_params = {
|
| 854 |
"model": model_name,
|
| 855 |
"messages": messages,
|
| 856 |
-
"max_tokens": MAX_TOKENS_CONFIG["deepseek"],
|
|
|
|
| 857 |
}
|
| 858 |
|
| 859 |
if not is_reasoning:
|
| 860 |
-
completion_params["temperature"] =
|
| 861 |
|
| 862 |
# Execute with retries
|
| 863 |
for attempt in range(max_retries):
|
|
@@ -919,18 +927,25 @@ def generate_legal_position(
|
|
| 919 |
# Prepare message creation parameters
|
| 920 |
message_params = {
|
| 921 |
"model": model_name,
|
| 922 |
-
"max_tokens": MAX_TOKENS_CONFIG["anthropic"],
|
| 923 |
"system": system_prompt,
|
| 924 |
"messages": messages,
|
| 925 |
-
"temperature":
|
| 926 |
}
|
| 927 |
|
| 928 |
-
# Add thinking config if enabled
|
| 929 |
-
if thinking_enabled and "claude" in model_name.lower()
|
| 930 |
-
|
| 931 |
-
|
| 932 |
-
"
|
| 933 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 934 |
|
| 935 |
# Retry logic for connection errors
|
| 936 |
max_retries = 3
|
|
@@ -1029,8 +1044,8 @@ def generate_legal_position(
|
|
| 1029 |
|
| 1030 |
# Build config based on model version
|
| 1031 |
config_params = {
|
| 1032 |
-
"temperature":
|
| 1033 |
-
"max_output_tokens": MAX_TOKENS_CONFIG["gemini"],
|
| 1034 |
"system_instruction": [
|
| 1035 |
types.Part.from_text(text=system_prompt),
|
| 1036 |
],
|
|
@@ -1207,13 +1222,17 @@ async def analyze_action(
|
|
| 1207 |
question: str,
|
| 1208 |
nodes: List[NodeWithScore],
|
| 1209 |
provider: str,
|
| 1210 |
-
model_name: str
|
|
|
|
|
|
|
| 1211 |
) -> str:
|
| 1212 |
"""Analyze search results using AI."""
|
| 1213 |
try:
|
| 1214 |
workflow = PrecedentAnalysisWorkflow(
|
| 1215 |
provider=ModelProvider(provider),
|
| 1216 |
-
model_name=AnalysisModelName(model_name)
|
|
|
|
|
|
|
| 1217 |
)
|
| 1218 |
|
| 1219 |
query = (
|
|
|
|
| 272 |
class LLMAnalyzer:
|
| 273 |
"""Class for handling different LLM providers."""
|
| 274 |
|
| 275 |
+
def __init__(self, provider: Any, model_name: Any, temperature: float = GENERATION_TEMPERATURE, max_tokens: Optional[int] = None):
|
| 276 |
self.provider = provider
|
| 277 |
self.model_name = model_name
|
| 278 |
+
self.temperature = temperature
|
| 279 |
+
self.max_tokens = max_tokens
|
| 280 |
|
| 281 |
if provider == ModelProvider.OPENAI:
|
| 282 |
if not OPENAI_API_KEY:
|
|
|
|
| 344 |
|
| 345 |
# Reasoning models usually require temperature=1.0 or none
|
| 346 |
if not is_reasoning_model:
|
| 347 |
+
completion_params["temperature"] = self.temperature
|
| 348 |
|
| 349 |
# Add GPT-5.2 specific parameters
|
| 350 |
if "gpt-5" in model_val.lower():
|
|
|
|
| 399 |
# Use JSON mode and temperature only for non-reasoning models
|
| 400 |
if not is_reasoning:
|
| 401 |
completion_params["response_format"] = {'type': 'json_object'}
|
| 402 |
+
completion_params["temperature"] = self.temperature
|
| 403 |
|
| 404 |
# Retry logic for DeepSeek analysis
|
| 405 |
max_retries = 3
|
|
|
|
| 431 |
try:
|
| 432 |
response = self.client.messages.create(
|
| 433 |
model=self.model_name,
|
| 434 |
+
max_tokens=self.max_tokens or MAX_TOKENS_ANALYSIS,
|
| 435 |
+
temperature=self.temperature,
|
| 436 |
system=SYSTEM_PROMPT,
|
| 437 |
messages=[{"role": "user", "content": prompt}]
|
| 438 |
)
|
|
|
|
| 476 |
]
|
| 477 |
|
| 478 |
generate_content_config = types.GenerateContentConfig(
|
| 479 |
+
temperature=self.temperature,
|
| 480 |
+
max_output_tokens=self.max_tokens or MAX_TOKENS_ANALYSIS,
|
| 481 |
system_instruction=[
|
| 482 |
types.Part.from_text(text=SYSTEM_PROMPT),
|
| 483 |
],
|
|
|
|
| 532 |
"""Workflow for analyzing legal precedents."""
|
| 533 |
|
| 534 |
def __init__(self, provider: Any = ModelProvider.OPENAI,
|
| 535 |
+
model_name: Any = AnalysisModelName.GPT4o_MINI,
|
| 536 |
+
temperature: float = GENERATION_TEMPERATURE,
|
| 537 |
+
max_tokens: Optional[int] = None):
|
| 538 |
super().__init__()
|
| 539 |
+
self.analyzer = LLMAnalyzer(provider, model_name, temperature, max_tokens)
|
| 540 |
|
| 541 |
@step
|
| 542 |
async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
|
|
|
|
| 617 |
provider: str,
|
| 618 |
model_name: str,
|
| 619 |
thinking_enabled: bool = False,
|
| 620 |
+
thinking_type: str = "Adaptive",
|
| 621 |
thinking_level: str = "MEDIUM",
|
| 622 |
+
openai_verbosity: str = "medium",
|
| 623 |
thinking_budget: int = 10000,
|
| 624 |
+
temperature: float = GENERATION_TEMPERATURE,
|
| 625 |
+
max_tokens: Optional[int] = None,
|
| 626 |
custom_system_prompt: Optional[str] = None,
|
| 627 |
custom_lp_prompt: Optional[str] = None
|
| 628 |
) -> Dict:
|
|
|
|
| 755 |
|
| 756 |
# Set tokens based on model capabilities
|
| 757 |
if is_reasoning_model:
|
| 758 |
+
completion_params["max_completion_tokens"] = max_tokens or MAX_TOKENS_CONFIG["openai"]
|
| 759 |
else:
|
| 760 |
+
completion_params["max_tokens"] = max_tokens or MAX_TOKENS_CONFIG["openai"]
|
| 761 |
+
completion_params["temperature"] = temperature
|
| 762 |
|
| 763 |
# Handle thinking/reasoning for GPT-5.2 and other reasoning models
|
| 764 |
if thinking_enabled and is_reasoning_model:
|
| 765 |
# GPT-5.2 specific parameters
|
| 766 |
if "gpt-5" in model_name.lower():
|
| 767 |
completion_params["reasoning_effort"] = thinking_level.lower()
|
| 768 |
+
completion_params["verbosity"] = openai_verbosity.lower()
|
| 769 |
completion_params["store"] = False
|
| 770 |
else:
|
| 771 |
# For other reasoning models (gpt-4.1, o1, etc.)
|
|
|
|
| 857 |
messages.append({"role": "user", "content": combined_content})
|
| 858 |
else:
|
| 859 |
messages.append({"role": "system", "content": system_prompt})
|
|
|
|
|
|
|
| 860 |
completion_params = {
|
| 861 |
"model": model_name,
|
| 862 |
"messages": messages,
|
| 863 |
+
"max_tokens": max_tokens or MAX_TOKENS_CONFIG["deepseek"],
|
| 864 |
+
"frequency_penalty": 0.0,
|
| 865 |
}
|
| 866 |
|
| 867 |
if not is_reasoning:
|
| 868 |
+
completion_params["temperature"] = temperature
|
| 869 |
|
| 870 |
# Execute with retries
|
| 871 |
for attempt in range(max_retries):
|
|
|
|
| 927 |
# Prepare message creation parameters
|
| 928 |
message_params = {
|
| 929 |
"model": model_name,
|
| 930 |
+
"max_tokens": max_tokens or MAX_TOKENS_CONFIG["anthropic"],
|
| 931 |
"system": system_prompt,
|
| 932 |
"messages": messages,
|
| 933 |
+
"temperature": temperature
|
| 934 |
}
|
| 935 |
|
| 936 |
+
# Add thinking config if enabled
|
| 937 |
+
if thinking_enabled and "claude" in model_name.lower():
|
| 938 |
+
# For Claude 4.6 models, we can use Adaptive
|
| 939 |
+
if thinking_type.lower() == "adaptive" and getattr(model_name, "find", lambda x: -1)("-4-6") != -1:
|
| 940 |
+
message_params["thinking"] = {"type": "adaptive"}
|
| 941 |
+
message_params["temperature"] = 1.0
|
| 942 |
+
else:
|
| 943 |
+
# 'Enabled' type works for both 4.5 and 4.6 models
|
| 944 |
+
message_params["thinking"] = {
|
| 945 |
+
"type": "enabled",
|
| 946 |
+
"budget_tokens": max(1024, int(thinking_budget))
|
| 947 |
+
}
|
| 948 |
+
message_params["temperature"] = 1.0
|
| 949 |
|
| 950 |
# Retry logic for connection errors
|
| 951 |
max_retries = 3
|
|
|
|
| 1044 |
|
| 1045 |
# Build config based on model version
|
| 1046 |
config_params = {
|
| 1047 |
+
"temperature": temperature,
|
| 1048 |
+
"max_output_tokens": max_tokens or MAX_TOKENS_CONFIG["gemini"],
|
| 1049 |
"system_instruction": [
|
| 1050 |
types.Part.from_text(text=system_prompt),
|
| 1051 |
],
|
|
|
|
| 1222 |
question: str,
|
| 1223 |
nodes: List[NodeWithScore],
|
| 1224 |
provider: str,
|
| 1225 |
+
model_name: str,
|
| 1226 |
+
temperature: float = GENERATION_TEMPERATURE,
|
| 1227 |
+
max_tokens: Optional[int] = None
|
| 1228 |
) -> str:
|
| 1229 |
"""Analyze search results using AI."""
|
| 1230 |
try:
|
| 1231 |
workflow = PrecedentAnalysisWorkflow(
|
| 1232 |
provider=ModelProvider(provider),
|
| 1233 |
+
model_name=AnalysisModelName(model_name),
|
| 1234 |
+
temperature=temperature,
|
| 1235 |
+
max_tokens=max_tokens
|
| 1236 |
)
|
| 1237 |
|
| 1238 |
query = (
|
prompts.py
CHANGED
|
@@ -8,7 +8,8 @@ SYSTEM_PROMPT = """<role>
|
|
| 8 |
</role>"""
|
| 9 |
|
| 10 |
# Main prompt template
|
| 11 |
-
LEGAL_POSITION_PROMPT = """
|
|
|
|
| 12 |
На основі наданого тексту судового рішення сформулюй правову позицію,
|
| 13 |
яка містить:
|
| 14 |
1. **Заголовок** — стисле формулювання суті правової позиції
|
|
@@ -27,27 +28,35 @@ LEGAL_POSITION_PROMPT = """<task>
|
|
| 27 |
|
| 28 |
<rules>
|
| 29 |
<rule id="abstraction">
|
| 30 |
-
Формулюй правову позицію як
|
| 31 |
до аналогічних справ. Не згадуй конкретних осіб, назви підприємств,
|
| 32 |
дати чи номери справ. Замість цього використовуй узагальнені терміни:
|
| 33 |
"особа", "позивач", "відповідач", "суб'єкт владних повноважень", "суд".
|
| 34 |
</rule>
|
| 35 |
|
| 36 |
-
<rule id="legal_references">
|
| 37 |
-
ОБОВ'ЯЗКОВО зберігай посилання на конкретні статті законів (КК, КПК, ЦК, ГК,
|
| 38 |
-
КАС, ЦПК, ГПК тощо). Посилання на статті — це ключова частина правової позиції,
|
| 39 |
-
яка забезпечує її юридичну точність і практичну застосовність.
|
| 40 |
-
Приклад: "відповідно до статті 116 КК України", "за змістом частини 1 статті 463 КПК".
|
| 41 |
-
</rule>
|
| 42 |
-
|
| 43 |
<rule id="conciseness">
|
| 44 |
-
Текст правової позиції має бути достатньо стислим і
|
|
|
|
| 45 |
Кожне слово повинно нести юридичний зміст. Уникай:
|
| 46 |
- вступних фраз ("слід зазначити що", "необхідно відмітити");
|
| 47 |
- повторення очевидного;
|
| 48 |
- зайвих пояснень, які не додають правового змісту.
|
| 49 |
</rule>
|
| 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
<rule id="language">
|
| 52 |
Використовуй ВИКЛЮЧНО українську мову. Дотримуйся офіційно-ділового стилю,
|
| 53 |
характерного для правових документів Верховного Суду України.
|
|
@@ -65,6 +74,7 @@ LEGAL_POSITION_PROMPT = """<task>
|
|
| 65 |
Категорія повинна бути конкретною і по можливості містити посилання на відповідні
|
| 66 |
статті кодексів. Категорія описує правову тематику, а не просто тип судочинства.
|
| 67 |
</rule>
|
|
|
|
| 68 |
</rules>
|
| 69 |
|
| 70 |
<output_format>
|
|
|
|
| 8 |
</role>"""
|
| 9 |
|
| 10 |
# Main prompt template
|
| 11 |
+
LEGAL_POSITION_PROMPT = """
|
| 12 |
+
<task>
|
| 13 |
На основі наданого тексту судового рішення сформулюй правову позицію,
|
| 14 |
яка містить:
|
| 15 |
1. **Заголовок** — стисле формулювання суті правової позиції
|
|
|
|
| 28 |
|
| 29 |
<rules>
|
| 30 |
<rule id="abstraction">
|
| 31 |
+
Формулюй правову позицію як ПРАВИЛО, придатне для застосування
|
| 32 |
до аналогічних справ. Не згадуй конкретних осіб, назви підприємств,
|
| 33 |
дати чи номери справ. Замість цього використовуй узагальнені терміни:
|
| 34 |
"особа", "позивач", "відповідач", "суб'єкт владних повноважень", "суд".
|
| 35 |
</rule>
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
<rule id="conciseness">
|
| 38 |
+
Текст правової позиції має бути достатньо стислим і лаконічним (1-2 абзаци).
|
| 39 |
+
Текст правової позиції має бути нормативним та логічно завершеним.
|
| 40 |
Кожне слово повинно нести юридичний зміст. Уникай:
|
| 41 |
- вступних фраз ("слід зазначити що", "необхідно відмітити");
|
| 42 |
- повторення очевидного;
|
| 43 |
- зайвих пояснень, які не додають правового змісту.
|
| 44 |
</rule>
|
| 45 |
|
| 46 |
+
<rule id="style_and_legal_technique">
|
| 47 |
+
Уникай надмірної деталізації процесуальних варіантів та повторення тексту закону.
|
| 48 |
+
</rule>
|
| 49 |
+
|
| 50 |
+
<rule id="recommended_language_patterns">
|
| 51 |
+
Використовуй типові нормативні конструкції, зокрема:
|
| 52 |
+
- "Суд, вирішуючи питання..., повинен враховувати..."
|
| 53 |
+
- "Сам по собі факт ... не є достатньою підставою для ..."
|
| 54 |
+
- "Оцінка має здійснюватися з урахуванням ..."
|
| 55 |
+
- "Застосування цієї норми передбачає ..."
|
| 56 |
+
- "Відмежування ... від ... має вирішальне значення для правильної правової кваліфікації"
|
| 57 |
+
- "Такий підхід забезпечує дотримання принципу ..."
|
| 58 |
+
</rule>
|
| 59 |
+
|
| 60 |
<rule id="language">
|
| 61 |
Використовуй ВИКЛЮЧНО українську мову. Дотримуйся офіційно-ділового стилю,
|
| 62 |
характерного для правових документів Верховного Суду України.
|
|
|
|
| 74 |
Категорія повинна бути конкретною і по можливості містити посилання на відповідні
|
| 75 |
статті кодексів. Категорія описує правову тематику, а не просто тип судочинства.
|
| 76 |
</rule>
|
| 77 |
+
|
| 78 |
</rules>
|
| 79 |
|
| 80 |
<output_format>
|
test_anthropic_rest.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import os
|
| 3 |
+
import httpx
|
| 4 |
+
import json
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
|
| 7 |
+
load_dotenv()
|
| 8 |
+
|
| 9 |
+
def test_rest():
|
| 10 |
+
api_key = os.getenv("ANTHROPIC_API_KEY")
|
| 11 |
+
url = "https://api.anthropic.com/v1/messages"
|
| 12 |
+
|
| 13 |
+
headers = {
|
| 14 |
+
"x-api-key": api_key,
|
| 15 |
+
"anthropic-version": "2023-06-01",
|
| 16 |
+
"anthropic-beta": "output-128k-2025-02-19", # test if needed
|
| 17 |
+
"content-type": "application/json"
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
payload = {
|
| 21 |
+
"model": "claude-opus-4-6",
|
| 22 |
+
"max_tokens": 4000,
|
| 23 |
+
"messages": [{"role": "user", "content": "Write a short poem about justice in 4 lines."}],
|
| 24 |
+
"temperature": 1.0,
|
| 25 |
+
"thinking": {"type": "adaptive"}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
# Also test effort
|
| 29 |
+
payload_effort = {
|
| 30 |
+
"model": "claude-opus-4-6",
|
| 31 |
+
"max_tokens": 4000,
|
| 32 |
+
"messages": [{"role": "user", "content": "Write a short poem about justice in 4 lines."}],
|
| 33 |
+
"temperature": 1.0,
|
| 34 |
+
"thinking": {"type": "adaptive", "effort": "low"}
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
client = httpx.Client(timeout=30.0)
|
| 38 |
+
print("Testing adaptive without effort...")
|
| 39 |
+
r = client.post(url, headers=headers, json=payload)
|
| 40 |
+
print("Status:", r.status_code)
|
| 41 |
+
if r.status_code != 200:
|
| 42 |
+
print(r.json())
|
| 43 |
+
|
| 44 |
+
print("\nTesting adaptive with effort parameter...")
|
| 45 |
+
r = client.post(url, headers=headers, json=payload_effort)
|
| 46 |
+
print("Status:", r.status_code)
|
| 47 |
+
if r.status_code != 200:
|
| 48 |
+
print(r.json())
|
| 49 |
+
|
| 50 |
+
if __name__ == "__main__":
|
| 51 |
+
test_rest()
|