DocUA commited on
Commit
0b3ee40
·
1 Parent(s): 8f359a6

feat: Add thinking parameters to Analysis mode and update models list in README

Browse files
Files changed (3) hide show
  1. README.md +6 -6
  2. interface.py +63 -2
  3. 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-4.1, GPT-4o, custom fine-tuned models
68
- - **Anthropic**: Claude 4.5 Sonnet (з підтримкою Extended Thinking)
69
- - **Google**: Gemini 3.0 Flash, 3.5 Flash (з підтримкою Thinking Mode)
70
  - **DeepSeek**: DeepSeek Chat
71
 
72
  ### Для аналізу:
73
- - **OpenAI**: GPT-5.2 (NEW! з reasoning), GPT-4.1, GPT-4o
74
- - **Anthropic**: Claude 4.5 Sonnet
75
- - **Google**: Gemini 3.0 Flash, 3.5 Flash
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
- response = self.client.messages.create(
517
- model=self.model_name,
518
- max_tokens=self.max_tokens or MAX_TOKENS_ANALYSIS,
519
- temperature=self.temperature,
520
- system=[{"type": "text", "text": SYSTEM_PROMPT, "cache_control": {"type": "ephemeral"}}],
521
- messages=[{"role": "user", "content": prompt}]
522
- )
523
- response_text = response.content[0].text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- generate_content_config = types.GenerateContentConfig(
566
- temperature=self.temperature,
567
- max_output_tokens=self.max_tokens or MAX_TOKENS_ANALYSIS,
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 = (