Spaces:
Running
Running
Add Antropic
Browse files
main.py
CHANGED
@@ -45,6 +45,7 @@ load_dotenv()
|
|
45 |
aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID")
|
46 |
aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY")
|
47 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
|
|
48 |
|
49 |
embed_model = OpenAIEmbedding(model_name="text-embedding-3-small")
|
50 |
Settings.embed_model = embed_model
|
@@ -127,13 +128,11 @@ class ModelProvider(str, Enum):
|
|
127 |
|
128 |
class ModelName(str, Enum):
|
129 |
# OpenAI models
|
130 |
-
|
131 |
-
|
132 |
-
GPT4_MINI = "gpt-4o-mini"
|
133 |
# Anthropic models
|
134 |
-
|
135 |
-
|
136 |
-
CLAUDE3_HAIKU = "claude-3-haiku-20240307"
|
137 |
|
138 |
|
139 |
class LLMAnalyzer:
|
@@ -142,9 +141,10 @@ class LLMAnalyzer:
|
|
142 |
self.model_name = model_name
|
143 |
|
144 |
if provider == ModelProvider.OPENAI:
|
145 |
-
self.client = OpenAI(model=model_name)
|
146 |
elif provider == ModelProvider.ANTHROPIC:
|
147 |
-
|
|
|
148 |
else:
|
149 |
raise ValueError(f"Unsupported provider: {provider}")
|
150 |
|
@@ -178,19 +178,26 @@ class LLMAnalyzer:
|
|
178 |
return response.message.content
|
179 |
|
180 |
async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
|
181 |
-
response =
|
182 |
model=self.model_name,
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
)
|
188 |
return response.content[0].text
|
189 |
|
190 |
|
191 |
class PrecedentAnalysisWorkflow(Workflow):
|
192 |
def __init__(self, provider: ModelProvider = ModelProvider.OPENAI,
|
193 |
-
model_name: ModelName = ModelName.
|
194 |
super().__init__()
|
195 |
self.analyzer = LLMAnalyzer(provider, model_name)
|
196 |
|
@@ -482,139 +489,291 @@ def generate_legal_position(court_decision_text, user_question):
|
|
482 |
}
|
483 |
|
484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
def create_gradio_interface():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
486 |
with gr.Blocks() as app:
|
|
|
487 |
gr.Markdown("# Аналізатор релевантних Правових Позицій Верховного Суду для нового судового рішення")
|
488 |
|
489 |
with gr.Row():
|
490 |
url_input = gr.Textbox(label="URL судового рішення:")
|
491 |
question_input = gr.Textbox(label="Уточнююче питання для аналізу:")
|
492 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
493 |
with gr.Row():
|
494 |
generate_position_button = gr.Button("Генерувати короткий зміст позиції суду")
|
495 |
search_with_ai_button = gr.Button("Пошук із ШІ", interactive=False)
|
496 |
-
# search_without_ai_button = gr.Button("Пошук без ШІ")
|
497 |
analyze_button = gr.Button("Аналіз", interactive=False)
|
498 |
|
499 |
position_output = gr.Markdown(label="Короткий зміст позиції суду за введеним рішенням")
|
500 |
search_output = gr.Markdown(label="Результат пошуку")
|
501 |
analysis_output = gr.Markdown(label="Результат аналізу")
|
502 |
|
503 |
-
# Два об'єкти стану для зберігання legal_position_json та nodes
|
504 |
state_lp_json = gr.State()
|
505 |
state_nodes = gr.State()
|
506 |
|
507 |
-
|
508 |
-
try:
|
509 |
-
court_decision_text = extract_court_decision_text(url)
|
510 |
-
legal_position_json = generate_legal_position(court_decision_text, "")
|
511 |
-
position_output_content = f"**Короткий зміст позиції суду за введеним рішенням:**\n *{legal_position_json['title']}*: \n{legal_position_json['text']} **Категорія:** \n{legal_position_json['category']} ({legal_position_json['proceeding']})\n\n"
|
512 |
-
return position_output_content, legal_position_json
|
513 |
-
except Exception as e:
|
514 |
-
return f"Error during position generation: {str(e)}", None
|
515 |
-
|
516 |
-
async def search_with_ai_action(legal_position_json):
|
517 |
-
try:
|
518 |
-
query_text = legal_position_json["title"] + ': ' + legal_position_json["text"] + ': ' + legal_position_json["proceeding"] + ': ' + legal_position_json["category"]
|
519 |
-
nodes = await retriever_bm25.aretrieve(query_text)
|
520 |
-
|
521 |
-
sources_output = "\n **Результати пошуку (наявні правові позиції ВСУ):** \n\n"
|
522 |
-
for index, node in enumerate(nodes, start=1):
|
523 |
-
source_title = node.node.metadata.get('title')
|
524 |
-
doc_ids = node.node.metadata.get('doc_id')
|
525 |
-
lp_ids = node.node.metadata.get('lp_id')
|
526 |
-
links = get_links_html(doc_ids)
|
527 |
-
links_lp = get_links_html_lp(lp_ids)
|
528 |
-
sources_output += f"\n[{index}] *{source_title}* {links_lp} 👉 Score: {node.score} {links}\n"
|
529 |
-
|
530 |
-
return sources_output, nodes
|
531 |
-
except Exception as e:
|
532 |
-
return f"Error during search: {str(e)}", None
|
533 |
-
|
534 |
-
async def search_without_ai_action(url):
|
535 |
-
try:
|
536 |
-
court_decision_text = extract_court_decision_text(url)
|
537 |
-
nodes = await retriever_bm25.aretrieve(court_decision_text)
|
538 |
-
|
539 |
-
search_output_content = f"**Результати пошуку (наявні правові позиції ВСУ):** \n\n"
|
540 |
-
for index, node in enumerate(nodes, start=1):
|
541 |
-
source_title = node.node.metadata.get('title', 'Невідомий заголовок')
|
542 |
-
doc_ids = node.node.metadata.get('doc_id')
|
543 |
-
links = get_links_html(doc_ids)
|
544 |
-
search_output_content += f"\n[{index}] *{source_title}* 👉 Score: {node.score} {links}\n"
|
545 |
-
|
546 |
-
return search_output_content, nodes
|
547 |
-
except Exception as e:
|
548 |
-
return f"Error during search: {str(e)}", None
|
549 |
-
|
550 |
-
async def analyze_action(legal_position_json, question, nodes):
|
551 |
-
try:
|
552 |
-
# Використання з OpenAI
|
553 |
-
workflow = PrecedentAnalysisWorkflow(
|
554 |
-
provider=ModelProvider.OPENAI,
|
555 |
-
model_name=ModelName.GPT4_MINI
|
556 |
-
)
|
557 |
-
|
558 |
-
# # Використання з Anthropic
|
559 |
-
# workflow_anthropic = PrecedentAnalysisWorkflow(
|
560 |
-
# provider=ModelProvider.ANTHROPIC,
|
561 |
-
# model_name=ModelName.CLAUDE3_SONNET
|
562 |
-
# )
|
563 |
-
|
564 |
-
# Формуємо єдиний текст запиту з legal_position_json
|
565 |
-
query = (
|
566 |
-
f"{legal_position_json['title']}: "
|
567 |
-
f"{legal_position_json['text']}: "
|
568 |
-
f"{legal_position_json['proceeding']}: "
|
569 |
-
f"{legal_position_json['category']}"
|
570 |
-
)
|
571 |
-
|
572 |
-
# Запускаємо workflow і отримуємо текст аналізу
|
573 |
-
response_text = await workflow.run(
|
574 |
-
query=query,
|
575 |
-
question=question,
|
576 |
-
nodes=nodes
|
577 |
-
)
|
578 |
-
|
579 |
-
# Формуємо вивід
|
580 |
-
output = f"**Аналіз ШІ:**\n{response_text}\n\n"
|
581 |
-
output += "**Наявні в базі Правові Позицій Верховного Суду:**\n\n"
|
582 |
-
|
583 |
-
# Розбиваємо текст відповіді на рядки
|
584 |
-
analysis_lines = response_text.split('\n')
|
585 |
-
|
586 |
-
# Проходимо по кожному рядку аналізу
|
587 |
-
for line in analysis_lines:
|
588 |
-
if line.startswith('* ['):
|
589 |
-
# З кожного рядка отримуємо індекс
|
590 |
-
index = line[3:line.index(']')] # Витягуємо індекс з "* [X]"
|
591 |
-
|
592 |
-
# Знаходимо відповідний node за індексом
|
593 |
-
node = nodes[int(index) - 1]
|
594 |
-
source_node = node.node
|
595 |
-
|
596 |
-
source_title = source_node.metadata.get('title', 'Невідомий заголовок')
|
597 |
-
source_text_lp = node.text
|
598 |
-
doc_ids = source_node.metadata.get('doc_id')
|
599 |
-
lp_id = source_node.metadata.get('lp_id')
|
600 |
-
|
601 |
-
links = get_links_html(doc_ids)
|
602 |
-
links_lp = get_links_html_lp(lp_id)
|
603 |
-
|
604 |
-
output += f"[{index}]: *{source_title}* | {source_text_lp} | {links_lp} | {links}\n\n"
|
605 |
-
|
606 |
-
return output
|
607 |
-
|
608 |
-
except Exception as e:
|
609 |
-
return f"Error during analysis: {str(e)}"
|
610 |
-
|
611 |
-
# Підключаємо функції до кнопок з оновленими входами та виходами
|
612 |
generate_position_button.click(
|
613 |
fn=generate_position_action,
|
614 |
inputs=url_input,
|
615 |
outputs=[position_output, state_lp_json]
|
616 |
-
)
|
617 |
-
generate_position_button.click(
|
618 |
fn=lambda: gr.update(interactive=True),
|
619 |
inputs=None,
|
620 |
outputs=search_with_ai_button
|
@@ -624,30 +783,24 @@ def create_gradio_interface():
|
|
624 |
fn=search_with_ai_action,
|
625 |
inputs=state_lp_json,
|
626 |
outputs=[search_output, state_nodes]
|
627 |
-
)
|
628 |
-
search_with_ai_button.click(
|
629 |
fn=lambda: gr.update(interactive=True),
|
630 |
inputs=None,
|
631 |
outputs=analyze_button
|
632 |
)
|
633 |
|
634 |
-
# search_without_ai_button.click(
|
635 |
-
# fn=search_without_ai_action,
|
636 |
-
# inputs=url_input,
|
637 |
-
# outputs=[search_output, state_nodes]
|
638 |
-
# )
|
639 |
-
# search_without_ai_button.click(
|
640 |
-
# fn=lambda: gr.update(interactive=True),
|
641 |
-
# inputs=None,
|
642 |
-
# outputs=analyze_button
|
643 |
-
# )
|
644 |
-
|
645 |
analyze_button.click(
|
646 |
fn=analyze_action,
|
647 |
-
inputs=[state_lp_json, question_input, state_nodes],
|
648 |
outputs=analysis_output
|
649 |
)
|
650 |
|
|
|
|
|
|
|
|
|
|
|
|
|
651 |
return app
|
652 |
|
653 |
if __name__ == "__main__":
|
|
|
45 |
aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID")
|
46 |
aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY")
|
47 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
48 |
+
anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")
|
49 |
|
50 |
embed_model = OpenAIEmbedding(model_name="text-embedding-3-small")
|
51 |
Settings.embed_model = embed_model
|
|
|
128 |
|
129 |
class ModelName(str, Enum):
|
130 |
# OpenAI models
|
131 |
+
GPT4o = "gpt-4o"
|
132 |
+
GPT4o_MINI = "gpt-4o-mini"
|
|
|
133 |
# Anthropic models
|
134 |
+
CLAUDE3_5_SONNET = "claude-3-5-sonnet-latest"
|
135 |
+
CLAUDE3_5_HAIKU = "claude-3-5-haiku-latest"
|
|
|
136 |
|
137 |
|
138 |
class LLMAnalyzer:
|
|
|
141 |
self.model_name = model_name
|
142 |
|
143 |
if provider == ModelProvider.OPENAI:
|
144 |
+
self.client = OpenAI(model=model_name)
|
145 |
elif provider == ModelProvider.ANTHROPIC:
|
146 |
+
# Додаємо API ключ при ініціалізації
|
147 |
+
self.client = Anthropic(api_key=anthropic_api_key)
|
148 |
else:
|
149 |
raise ValueError(f"Unsupported provider: {provider}")
|
150 |
|
|
|
178 |
return response.message.content
|
179 |
|
180 |
async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
|
181 |
+
response = self.client.messages.create( # Прибрали await
|
182 |
model=self.model_name,
|
183 |
+
max_tokens=2000,
|
184 |
+
messages=[
|
185 |
+
{
|
186 |
+
"role": "assistant",
|
187 |
+
"content": "Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду."
|
188 |
+
},
|
189 |
+
{
|
190 |
+
"role": "user",
|
191 |
+
"content": prompt
|
192 |
+
}
|
193 |
+
]
|
194 |
)
|
195 |
return response.content[0].text
|
196 |
|
197 |
|
198 |
class PrecedentAnalysisWorkflow(Workflow):
|
199 |
def __init__(self, provider: ModelProvider = ModelProvider.OPENAI,
|
200 |
+
model_name: ModelName = ModelName.GPT4o_MINI):
|
201 |
super().__init__()
|
202 |
self.analyzer = LLMAnalyzer(provider, model_name)
|
203 |
|
|
|
489 |
}
|
490 |
|
491 |
|
492 |
+
# def create_gradio_interface():
|
493 |
+
# with gr.Blocks() as app:
|
494 |
+
# gr.Markdown("# Аналізатор релевантних Правових Позицій Верховного Суду для нового судового рішення")
|
495 |
+
#
|
496 |
+
# with gr.Row():
|
497 |
+
# url_input = gr.Textbox(label="URL судового рішення:")
|
498 |
+
# question_input = gr.Textbox(label="Уточнююче питання для аналізу:")
|
499 |
+
#
|
500 |
+
# with gr.Row():
|
501 |
+
# generate_position_button = gr.Button("Генерувати короткий зміст позиції суду")
|
502 |
+
# search_with_ai_button = gr.Button("Пошук із ШІ", interactive=False)
|
503 |
+
# # search_without_ai_button = gr.Button("Пошук без ШІ")
|
504 |
+
# analyze_button = gr.Button("Аналіз", interactive=False)
|
505 |
+
#
|
506 |
+
# position_output = gr.Markdown(label="Короткий зміст позиції суду за введеним рішенням")
|
507 |
+
# search_output = gr.Markdown(label="Результат пошуку")
|
508 |
+
# analysis_output = gr.Markdown(label="Результат аналізу")
|
509 |
+
#
|
510 |
+
# # Два об'єкти стану для зберігання legal_position_json та nodes
|
511 |
+
# state_lp_json = gr.State()
|
512 |
+
# state_nodes = gr.State()
|
513 |
+
#
|
514 |
+
# async def generate_position_action(url):
|
515 |
+
# try:
|
516 |
+
# court_decision_text = extract_court_decision_text(url)
|
517 |
+
# legal_position_json = generate_legal_position(court_decision_text, "")
|
518 |
+
# position_output_content = f"**Короткий зміст позиції суду за введеним рішенням:**\n *{legal_position_json['title']}*: \n{legal_position_json['text']} **Категорія:** \n{legal_position_json['category']} ({legal_position_json['proceeding']})\n\n"
|
519 |
+
# return position_output_content, legal_position_json
|
520 |
+
# except Exception as e:
|
521 |
+
# return f"Error during position generation: {str(e)}", None
|
522 |
+
#
|
523 |
+
# async def search_with_ai_action(legal_position_json):
|
524 |
+
# try:
|
525 |
+
# query_text = legal_position_json["title"] + ': ' + legal_position_json["text"] + ': ' + legal_position_json["proceeding"] + ': ' + legal_position_json["category"]
|
526 |
+
# nodes = await retriever_bm25.aretrieve(query_text)
|
527 |
+
#
|
528 |
+
# sources_output = "\n **Результати пошуку (наявні правові позиції ВСУ):** \n\n"
|
529 |
+
# for index, node in enumerate(nodes, start=1):
|
530 |
+
# source_title = node.node.metadata.get('title')
|
531 |
+
# doc_ids = node.node.metadata.get('doc_id')
|
532 |
+
# lp_ids = node.node.metadata.get('lp_id')
|
533 |
+
# links = get_links_html(doc_ids)
|
534 |
+
# links_lp = get_links_html_lp(lp_ids)
|
535 |
+
# sources_output += f"\n[{index}] *{source_title}* {links_lp} 👉 Score: {node.score} {links}\n"
|
536 |
+
#
|
537 |
+
# return sources_output, nodes
|
538 |
+
# except Exception as e:
|
539 |
+
# return f"Error during search: {str(e)}", None
|
540 |
+
#
|
541 |
+
# async def search_without_ai_action(url):
|
542 |
+
# try:
|
543 |
+
# court_decision_text = extract_court_decision_text(url)
|
544 |
+
# nodes = await retriever_bm25.aretrieve(court_decision_text)
|
545 |
+
#
|
546 |
+
# search_output_content = f"**Результати пошуку (наявні правові позиції ВСУ):** \n\n"
|
547 |
+
# for index, node in enumerate(nodes, start=1):
|
548 |
+
# source_title = node.node.metadata.get('title', 'Невідомий заголовок')
|
549 |
+
# doc_ids = node.node.metadata.get('doc_id')
|
550 |
+
# links = get_links_html(doc_ids)
|
551 |
+
# search_output_content += f"\n[{index}] *{source_title}* 👉 Score: {node.score} {links}\n"
|
552 |
+
#
|
553 |
+
# return search_output_content, nodes
|
554 |
+
# except Exception as e:
|
555 |
+
# return f"Error during search: {str(e)}", None
|
556 |
+
#
|
557 |
+
# async def analyze_action(legal_position_json, question, nodes):
|
558 |
+
# try:
|
559 |
+
# # Використання з OpenAI
|
560 |
+
# workflow = PrecedentAnalysisWorkflow(
|
561 |
+
# provider=ModelProvider.OPENAI,
|
562 |
+
# model_name=ModelName.GPT4_MINI
|
563 |
+
# )
|
564 |
+
#
|
565 |
+
# # # Використання з Anthropic
|
566 |
+
# # workflow_anthropic = PrecedentAnalysisWorkflow(
|
567 |
+
# # provider=ModelProvider.ANTHROPIC,
|
568 |
+
# # model_name=ModelName.CLAUDE3_SONNET
|
569 |
+
# # )
|
570 |
+
#
|
571 |
+
# # Формуємо єдиний текст запиту з legal_position_json
|
572 |
+
# query = (
|
573 |
+
# f"{legal_position_json['title']}: "
|
574 |
+
# f"{legal_position_json['text']}: "
|
575 |
+
# f"{legal_position_json['proceeding']}: "
|
576 |
+
# f"{legal_position_json['category']}"
|
577 |
+
# )
|
578 |
+
#
|
579 |
+
# # Запускаємо workflow і отримуємо текст аналізу
|
580 |
+
# response_text = await workflow.run(
|
581 |
+
# query=query,
|
582 |
+
# question=question,
|
583 |
+
# nodes=nodes
|
584 |
+
# )
|
585 |
+
#
|
586 |
+
# # Формуємо вивід
|
587 |
+
# output = f"**Аналіз ШІ:**\n{response_text}\n\n"
|
588 |
+
# output += "**Наявні в базі Правові Позицій Верховного Суду:**\n\n"
|
589 |
+
#
|
590 |
+
# # Розбиваємо текст відповіді на рядки
|
591 |
+
# analysis_lines = response_text.split('\n')
|
592 |
+
#
|
593 |
+
# # Проходимо по кожному рядку аналізу
|
594 |
+
# for line in analysis_lines:
|
595 |
+
# if line.startswith('* ['):
|
596 |
+
# # З кожного рядка отримуємо індекс
|
597 |
+
# index = line[3:line.index(']')] # Витягуємо індекс з "* [X]"
|
598 |
+
#
|
599 |
+
# # Знаходимо відповідний node за індексом
|
600 |
+
# node = nodes[int(index) - 1]
|
601 |
+
# source_node = node.node
|
602 |
+
#
|
603 |
+
# source_title = source_node.metadata.get('title', 'Невідомий заголовок')
|
604 |
+
# source_text_lp = node.text
|
605 |
+
# doc_ids = source_node.metadata.get('doc_id')
|
606 |
+
# lp_id = source_node.metadata.get('lp_id')
|
607 |
+
#
|
608 |
+
# links = get_links_html(doc_ids)
|
609 |
+
# links_lp = get_links_html_lp(lp_id)
|
610 |
+
#
|
611 |
+
# output += f"[{index}]: *{source_title}* | {source_text_lp} | {links_lp} | {links}\n\n"
|
612 |
+
#
|
613 |
+
# return output
|
614 |
+
#
|
615 |
+
# except Exception as e:
|
616 |
+
# return f"Error during analysis: {str(e)}"
|
617 |
+
#
|
618 |
+
# # Підключаємо функції до кнопок з оновленими входами та виходами
|
619 |
+
# generate_position_button.click(
|
620 |
+
# fn=generate_position_action,
|
621 |
+
# inputs=url_input,
|
622 |
+
# outputs=[position_output, state_lp_json]
|
623 |
+
# )
|
624 |
+
# generate_position_button.click(
|
625 |
+
# fn=lambda: gr.update(interactive=True),
|
626 |
+
# inputs=None,
|
627 |
+
# outputs=search_with_ai_button
|
628 |
+
# )
|
629 |
+
#
|
630 |
+
# search_with_ai_button.click(
|
631 |
+
# fn=search_with_ai_action,
|
632 |
+
# inputs=state_lp_json,
|
633 |
+
# outputs=[search_output, state_nodes]
|
634 |
+
# )
|
635 |
+
# search_with_ai_button.click(
|
636 |
+
# fn=lambda: gr.update(interactive=True),
|
637 |
+
# inputs=None,
|
638 |
+
# outputs=analyze_button
|
639 |
+
# )
|
640 |
+
#
|
641 |
+
# # search_without_ai_button.click(
|
642 |
+
# # fn=search_without_ai_action,
|
643 |
+
# # inputs=url_input,
|
644 |
+
# # outputs=[search_output, state_nodes]
|
645 |
+
# # )
|
646 |
+
# # search_without_ai_button.click(
|
647 |
+
# # fn=lambda: gr.update(interactive=True),
|
648 |
+
# # inputs=None,
|
649 |
+
# # outputs=analyze_button
|
650 |
+
# # )
|
651 |
+
#
|
652 |
+
# analyze_button.click(
|
653 |
+
# fn=analyze_action,
|
654 |
+
# inputs=[state_lp_json, question_input, state_nodes],
|
655 |
+
# outputs=analysis_output
|
656 |
+
# )
|
657 |
+
#
|
658 |
+
# return app
|
659 |
+
|
660 |
def create_gradio_interface():
|
661 |
+
async def generate_position_action(url):
|
662 |
+
try:
|
663 |
+
court_decision_text = extract_court_decision_text(url)
|
664 |
+
legal_position_json = generate_legal_position(court_decision_text, "")
|
665 |
+
position_output_content = f"**Короткий зміст позиції суду за введеним рішенням:**\n *{legal_position_json['title']}*: \n{legal_position_json['text']} **Категорія:** \n{legal_position_json['category']} ({legal_position_json['proceeding']})\n\n"
|
666 |
+
return position_output_content, legal_position_json
|
667 |
+
except Exception as e:
|
668 |
+
return f"Error during position generation: {str(e)}", None
|
669 |
+
|
670 |
+
async def search_with_ai_action(legal_position_json):
|
671 |
+
try:
|
672 |
+
query_text = legal_position_json["title"] + ': ' + legal_position_json["text"] + ': ' + legal_position_json["proceeding"] + ': ' + legal_position_json["category"]
|
673 |
+
nodes = await retriever_bm25.aretrieve(query_text)
|
674 |
+
|
675 |
+
sources_output = "\n **Результати пошуку (наявні правові позиції ВСУ):** \n\n"
|
676 |
+
for index, node in enumerate(nodes, start=1):
|
677 |
+
source_title = node.node.metadata.get('title')
|
678 |
+
doc_ids = node.node.metadata.get('doc_id')
|
679 |
+
lp_ids = node.node.metadata.get('lp_id')
|
680 |
+
links = get_links_html(doc_ids)
|
681 |
+
links_lp = get_links_html_lp(lp_ids)
|
682 |
+
sources_output += f"\n[{index}] *{source_title}* {links_lp} 👉 Score: {node.score} {links}\n"
|
683 |
+
|
684 |
+
return sources_output, nodes
|
685 |
+
except Exception as e:
|
686 |
+
return f"Error during search: {str(e)}", None
|
687 |
+
|
688 |
+
async def analyze_action(legal_position_json, question, nodes, provider, model_name):
|
689 |
+
try:
|
690 |
+
workflow = PrecedentAnalysisWorkflow(
|
691 |
+
provider=ModelProvider(provider),
|
692 |
+
model_name=ModelName(model_name)
|
693 |
+
)
|
694 |
+
|
695 |
+
query = (
|
696 |
+
f"{legal_position_json['title']}: "
|
697 |
+
f"{legal_position_json['text']}: "
|
698 |
+
f"{legal_position_json['proceeding']}: "
|
699 |
+
f"{legal_position_json['category']}"
|
700 |
+
)
|
701 |
+
|
702 |
+
response_text = await workflow.run(
|
703 |
+
query=query,
|
704 |
+
question=question,
|
705 |
+
nodes=nodes
|
706 |
+
)
|
707 |
+
|
708 |
+
output = f"**Аналіз ШІ (модель: {model_name}):**\n{response_text}\n\n"
|
709 |
+
output += "**Наявні в базі Правові Позицій Верховного Суду:**\n\n"
|
710 |
+
|
711 |
+
analysis_lines = response_text.split('\n')
|
712 |
+
for line in analysis_lines:
|
713 |
+
if line.startswith('* ['):
|
714 |
+
index = line[3:line.index(']')]
|
715 |
+
node = nodes[int(index) - 1]
|
716 |
+
source_node = node.node
|
717 |
+
|
718 |
+
source_title = source_node.metadata.get('title', 'Невідомий заголовок')
|
719 |
+
source_text_lp = node.text
|
720 |
+
doc_ids = source_node.metadata.get('doc_id')
|
721 |
+
lp_id = source_node.metadata.get('lp_id')
|
722 |
+
|
723 |
+
links = get_links_html(doc_ids)
|
724 |
+
links_lp = get_links_html_lp(lp_id)
|
725 |
+
|
726 |
+
output += f"[{index}]: *{source_title}* | {source_text_lp} | {links_lp} | {links}\n\n"
|
727 |
+
|
728 |
+
return output
|
729 |
+
|
730 |
+
except Exception as e:
|
731 |
+
return f"Error during analysis: {str(e)}"
|
732 |
+
|
733 |
+
def update_model_choices(provider):
|
734 |
+
if provider == ModelProvider.OPENAI.value:
|
735 |
+
return gr.Dropdown(choices=[m.value for m in ModelName if m.value.startswith("gpt")])
|
736 |
+
else:
|
737 |
+
return gr.Dropdown(choices=[m.value for m in ModelName if m.value.startswith("claude")])
|
738 |
+
|
739 |
with gr.Blocks() as app:
|
740 |
+
# Далі ваш код інтерфейсу...
|
741 |
gr.Markdown("# Аналізатор релевантних Правових Позицій Верховного Суду для нового судового рішення")
|
742 |
|
743 |
with gr.Row():
|
744 |
url_input = gr.Textbox(label="URL судового рішення:")
|
745 |
question_input = gr.Textbox(label="Уточнююче питання для аналізу:")
|
746 |
|
747 |
+
with gr.Row():
|
748 |
+
provider_dropdown = gr.Dropdown(
|
749 |
+
choices=[p.value for p in ModelProvider],
|
750 |
+
value=ModelProvider.OPENAI.value,
|
751 |
+
label="Провайдер AI",
|
752 |
+
)
|
753 |
+
model_dropdown = gr.Dropdown(
|
754 |
+
choices=[m.value for m in ModelName if m.value.startswith("gpt")],
|
755 |
+
value=ModelName.GPT4o_MINI.value,
|
756 |
+
label="Модель",
|
757 |
+
)
|
758 |
+
|
759 |
with gr.Row():
|
760 |
generate_position_button = gr.Button("Генерувати короткий зміст позиції суду")
|
761 |
search_with_ai_button = gr.Button("Пошук із ШІ", interactive=False)
|
|
|
762 |
analyze_button = gr.Button("Аналіз", interactive=False)
|
763 |
|
764 |
position_output = gr.Markdown(label="Короткий зміст позиції суду за введеним рішенням")
|
765 |
search_output = gr.Markdown(label="Результат пошуку")
|
766 |
analysis_output = gr.Markdown(label="Результат аналізу")
|
767 |
|
|
|
768 |
state_lp_json = gr.State()
|
769 |
state_nodes = gr.State()
|
770 |
|
771 |
+
# Підключення функцій до кнопок
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
772 |
generate_position_button.click(
|
773 |
fn=generate_position_action,
|
774 |
inputs=url_input,
|
775 |
outputs=[position_output, state_lp_json]
|
776 |
+
).then(
|
|
|
777 |
fn=lambda: gr.update(interactive=True),
|
778 |
inputs=None,
|
779 |
outputs=search_with_ai_button
|
|
|
783 |
fn=search_with_ai_action,
|
784 |
inputs=state_lp_json,
|
785 |
outputs=[search_output, state_nodes]
|
786 |
+
).then(
|
|
|
787 |
fn=lambda: gr.update(interactive=True),
|
788 |
inputs=None,
|
789 |
outputs=analyze_button
|
790 |
)
|
791 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
792 |
analyze_button.click(
|
793 |
fn=analyze_action,
|
794 |
+
inputs=[state_lp_json, question_input, state_nodes, provider_dropdown, model_dropdown],
|
795 |
outputs=analysis_output
|
796 |
)
|
797 |
|
798 |
+
provider_dropdown.change(
|
799 |
+
fn=update_model_choices,
|
800 |
+
inputs=provider_dropdown,
|
801 |
+
outputs=model_dropdown
|
802 |
+
)
|
803 |
+
|
804 |
return app
|
805 |
|
806 |
if __name__ == "__main__":
|