DocUA commited on
Commit
a765e3e
·
1 Parent(s): 492b465

feat: Оновлення UI для GPT-5.2 та додавання контролю MaxTokens

Browse files
Files changed (7) hide show
  1. HELP.md +29 -18
  2. config/environments/default.yaml +19 -15
  3. config/models.py +6 -7
  4. interface.py +75 -17
  5. main.py +47 -28
  6. prompts.py +20 -10
  7. 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 3.5 Sonnet, GPT-4o
45
  - Для економії: DeepSeek Chat
46
 
47
- #### 2. Режим Thinking (для Gemini 3+ та Claude 4.5+)
48
- - Увімкніть для глибшого аналізу складних рішень
49
- - Рівні thinking (Gemini): Minimal Low Medium High
50
- - Бюджет токенів (Claude): 1000-20000
 
 
 
 
 
 
51
 
52
  #### 3. Спосіб вводу даних
53
 
@@ -134,10 +140,11 @@
134
  #### 1. Попередні кроки
135
  ⚠️ Спочатку потрібно виконати пошук у закладці "Пошук"
136
 
137
- #### 2. Вибір моделі аналізу
138
  - Оберіть провайдер та модель для аналізу
139
- - Може відрізнятися від моделі генерації
140
- - Рекомендовано: GPT-4o, Claude 3.5 Sonnet
 
141
 
142
  #### 3. Уточнююче питання (опціонально)
143
  Додайте конкретне питання для AI, наприклад:
@@ -334,28 +341,32 @@ df['text'] = df['parsed'].apply(lambda x: x['text'])
334
  ### OpenAI
335
 
336
  **Моделі:**
337
- - GPT-4o - найпотужніша модель
 
 
338
  - GPT-4o-mini - баланс ціна/якість
339
- - GPT-4.1 - нова версія GPT-4
340
  - Fine-tuned моделі - власні налаштовані моделі
341
 
342
  **Особливості:**
343
  - Швидка обробка
344
- - Висока якість
345
- - Підтримка JSON Schema
346
 
347
  **API Key:** `OPENAI_API_KEY`
348
 
349
  ### Anthropic (Claude)
350
 
351
  **Моделі:**
352
- - Claude 3.5 Sonnet - рекомендована
353
- - Claude 4.5 Sonnet - з Extended Thinking
 
354
 
355
  **Особливості:**
356
- - Детальний аналіз
357
- - Extended Thinking для складних завдань
358
- - Великий контекст (200K токенів)
 
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: 2048
27
- anthropic: 2048
28
- gemini: 2048
29
- deepseek: 2048
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-5-20251101"
58
- display_name: "Claude Opus 4.5"
 
 
 
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-3-7-sonnet-20250219"
89
- display_name: "Claude 3.7 Sonnet"
90
- - name: "claude-opus-4-5-20251101"
91
- display_name: "Claude Opus 4.5"
 
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-3-7-sonnet-20250219':
82
- return 'CLAUDE_SONNET_3_7'
83
- elif model_name == 'claude-opus-4-5-20251101':
84
- return 'CLAUDE_OPUS_4_5'
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.GPT4_1.value,
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.CLAUDE_SONNET_4_5.value,
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.GPT4_1.value,
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.CLAUDE_SONNET_4_5.value,
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("⚙️ Додаткові параметри (Thinking Mode)", open=False) as thinking_accordion:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
613
  thinking_enabled_checkbox = gr.Checkbox(
614
  label="Увімкнути режим Thinking (глибокий аналіз)",
615
  value=False,
616
- info="Активує розширений ланцюг міркувань для моделей Gemini 3+ та Claude 4.5"
617
  )
618
  with gr.Row():
 
 
 
 
 
 
619
  thinking_level_dropdown = gr.Dropdown(
620
- choices=["Minimal", "Low", "Medium", "High"],
621
- value="Medium",
622
- label="Рівень Thinking (Gemini)",
623
  interactive=False
624
  )
 
 
 
 
 
 
625
  thinking_budget_slider = gr.Slider(
626
- minimum=1000,
627
- maximum=20000,
628
  value=10000,
629
- step=1000,
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"] = 0
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"] = 0
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=GENERATION_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"] = GENERATION_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"] = "medium" # Can be "low", "medium", "high"
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"] = GENERATION_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": GENERATION_TEMPERATURE
926
  }
927
 
928
- # Add thinking config if enabled (only for Claude 4.5+ models)
929
- if thinking_enabled and "claude" in model_name.lower() and "-4-5-" in model_name:
930
- message_params["thinking"] = {
931
- "type": "enabled",
932
- "budget_tokens": int(thinking_budget)
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": GENERATION_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 = """<task>
 
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()