DocUA commited on
Commit
7d1496e
·
1 Parent(s): befab8d

Add Antropic

Browse files
Files changed (2) hide show
  1. main.py +152 -42
  2. requirements.txt +1 -0
main.py CHANGED
@@ -13,6 +13,8 @@ from pathlib import Path
13
  from bs4 import BeautifulSoup
14
  from typing import Union, List
15
  import asyncio
 
 
16
  from llama_index.core import (
17
  StorageContext,
18
  ServiceContext,
@@ -115,60 +117,159 @@ state_lp_json = gr.State()
115
  state_nodes = gr.State()
116
 
117
 
118
- class PrecedentAnalysisWorkflow(Workflow):
119
- @step
120
- async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
121
- query = ev.get("query") # нове рішення
122
- question = ev.get("question") # уточнююче питання
123
- nodes = ev.get("nodes") # знайдені правові позиції
124
 
125
- if not all([query, nodes]):
126
- return StopEvent(result="Недостатньо даних для аналізу. Необхідні нове рішення та правові позиції.")
 
127
 
128
- llm_analyse = OpenAI(model="gpt-4o", temperature=0)
129
- # llm_analyse = OpenAI(model="gpt-4o-mini", temperature=0)
130
 
131
- # Підготовка контексту та збір ID правових позицій
132
- context_parts = []
 
 
 
 
 
 
 
133
 
134
- for i, node in enumerate(nodes, 1):
135
- # Отримуємо текст з node.node якщо це NodeWithScore
136
- node_text = node.node.text if hasattr(node, 'node') else node.text
137
- # Отримуємо metadata з node.node якщо це NodeWithScore
138
- metadata = node.node.metadata if hasattr(node, 'node') else node.metadata
139
 
140
- lp_id = metadata.get('lp_id', f'unknown_{i}')
141
- source_index = str(i)
 
 
142
 
143
- context_parts.append(f"Source {source_index} (ID: {lp_id}):\n{node_text}")
 
 
 
 
 
144
 
145
- context_str = "\n\n".join(context_parts)
 
 
 
 
 
 
 
 
 
 
 
146
 
 
147
  response_format = {
148
  "type": "json_schema",
149
  "json_schema": {
150
- "name": "relevant_positions_schema",
151
- "schema": {
152
- "type": "object",
153
- "properties": {
154
- "relevant_positions": {
155
- "type": "array",
156
- "items": {
157
- "type": "object",
158
- "properties": {
159
- "lp_id": {"type": "string"},
160
- "source_index": {"type": "string"},
161
- "description": {"type": "string"}
162
- },
163
- "required": ["lp_id", "source_index", "description"]
164
- }
165
- }
166
- },
167
- "required": ["relevant_positions"]
168
- }
169
  }
170
  }
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  # Формування промпту та отримання відповіді
173
  prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
174
  query=query,
@@ -446,10 +547,19 @@ def create_gradio_interface():
446
  except Exception as e:
447
  return f"Error during search: {str(e)}", None
448
 
449
-
450
  async def analyze_action(legal_position_json, question, nodes):
451
  try:
452
- workflow = PrecedentAnalysisWorkflow(timeout=600)
 
 
 
 
 
 
 
 
 
 
453
 
454
  # Формуємо єдиний текст запиту з legal_position_json
455
  query = (
 
13
  from bs4 import BeautifulSoup
14
  from typing import Union, List
15
  import asyncio
16
+ from anthropic import Anthropic
17
+ from openai import OpenAI
18
  from llama_index.core import (
19
  StorageContext,
20
  ServiceContext,
 
117
  state_nodes = gr.State()
118
 
119
 
120
+ from enum import Enum
121
+
 
 
 
 
122
 
123
+ class ModelProvider(str, Enum):
124
+ OPENAI = "openai"
125
+ ANTHROPIC = "anthropic"
126
 
 
 
127
 
128
+ class ModelName(str, Enum):
129
+ # OpenAI models
130
+ GPT4 = "gpt-4"
131
+ GPT4_TURBO = "gpt-4-turbo-preview"
132
+ GPT4_MINI = "gpt-4o-mini"
133
+ # Anthropic models
134
+ CLAUDE3_SONNET = "claude-3-sonnet-20240229"
135
+ CLAUDE3_OPUS = "claude-3-opus-20240229"
136
+ CLAUDE3_HAIKU = "claude-3-haiku-20240307"
137
 
 
 
 
 
 
138
 
139
+ class LLMAnalyzer:
140
+ def __init__(self, provider: ModelProvider, model_name: ModelName):
141
+ self.provider = provider
142
+ self.model_name = model_name
143
 
144
+ if provider == ModelProvider.OPENAI:
145
+ self.client = OpenAI(model=model_name) # Використовуємо LlamaOpenAI
146
+ elif provider == ModelProvider.ANTHROPIC:
147
+ self.client = Anthropic()
148
+ else:
149
+ raise ValueError(f"Unsupported provider: {provider}")
150
 
151
+ async def analyze(self, prompt: str, response_schema: dict) -> str:
152
+ if self.provider == ModelProvider.OPENAI:
153
+ return await self._analyze_with_openai(prompt, response_schema)
154
+ else:
155
+ return await self._analyze_with_anthropic(prompt, response_schema)
156
+
157
+ async def _analyze_with_openai(self, prompt: str, response_schema: dict) -> str:
158
+ messages = [
159
+ ChatMessage(role="system",
160
+ content="Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду."),
161
+ ChatMessage(role="user", content=prompt)
162
+ ]
163
 
164
+ # Правильний формат для response_format
165
  response_format = {
166
  "type": "json_schema",
167
  "json_schema": {
168
+ "name": "relevant_positions_schema", # Додаємо обов'язкове поле name
169
+ "schema": response_schema
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  }
171
  }
172
 
173
+ response = self.client.chat(
174
+ messages=messages,
175
+ response_format=response_format,
176
+ temperature=0
177
+ )
178
+ return response.message.content
179
+
180
+ async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
181
+ response = await self.client.messages.create(
182
+ model=self.model_name,
183
+ temperature=0,
184
+ system="Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду.",
185
+ messages=[{"role": "user", "content": prompt}],
186
+ response_format={"type": "json_schema", "schema": response_schema}
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.GPT4_MINI):
194
+ super().__init__()
195
+ self.analyzer = LLMAnalyzer(provider, model_name)
196
+
197
+ @step
198
+ async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
199
+ try:
200
+ # Отримуємо параметри з події з дефолтними значеннями
201
+ query = ev.get("query", "")
202
+ question = ev.get("question", "")
203
+ nodes = ev.get("nodes", [])
204
+
205
+ # Перевірка на пусті значення
206
+ if not query:
207
+ return StopEvent(result="Помилка: Не надано текст нового рішення (query)")
208
+
209
+ if not nodes:
210
+ return StopEvent(result="Помилка: Не надано правові позиції для аналізу (nodes)")
211
+
212
+ # Підготовка контексту
213
+ context_parts = []
214
+ for i, node in enumerate(nodes, 1):
215
+ node_text = node.node.text if hasattr(node, 'node') else node.text
216
+ metadata = node.node.metadata if hasattr(node, 'node') else node.metadata
217
+ lp_id = metadata.get('lp_id', f'unknown_{i}')
218
+ context_parts.append(f"Source {i} (ID: {lp_id}):\n{node_text}")
219
+
220
+ context_str = "\n\n".join(context_parts)
221
+
222
+ # Схема відповіді
223
+ response_schema = {
224
+ "type": "object",
225
+ "properties": {
226
+ "relevant_positions": {
227
+ "type": "array",
228
+ "items": {
229
+ "type": "object",
230
+ "properties": {
231
+ "lp_id": {"type": "string"},
232
+ "source_index": {"type": "string"},
233
+ "description": {"type": "string"}
234
+ },
235
+ "required": ["lp_id", "source_index", "description"]
236
+ }
237
+ }
238
+ },
239
+ "required": ["relevant_positions"]
240
+ }
241
+
242
+ # Формування промпту
243
+ prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
244
+ query=query,
245
+ question=question if question else "Загальний аналіз релевантності",
246
+ context_str=context_str
247
+ )
248
+
249
+ # Отримання відповіді від моделі
250
+ response_content = await self.analyzer.analyze(prompt, response_schema)
251
+
252
+ try:
253
+ parsed_response = json.loads(response_content)
254
+ if "relevant_positions" in parsed_response:
255
+ response_lines = []
256
+ for position in parsed_response["relevant_positions"]:
257
+ position_text = (
258
+ f"* [{position['source_index']}] {position['description']} "
259
+ )
260
+ response_lines.append(position_text)
261
+
262
+ response_text = "\n".join(response_lines)
263
+ return StopEvent(result=response_text)
264
+ else:
265
+ return StopEvent(result="Не знайдено релевантних правових позицій")
266
+
267
+ except json.JSONDecodeError:
268
+ return StopEvent(result="Помилка обробки відповіді від AI")
269
+
270
+ except Exception as e:
271
+ return StopEvent(result=f"Error during analysis: {str(e)}")
272
+
273
  # Формування промпту та отримання відповіді
274
  prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
275
  query=query,
 
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 = (
requirements.txt CHANGED
@@ -3,6 +3,7 @@ llama-index-readers-file
3
  llama-index-vector-stores-faiss
4
  llama-index-retrievers-bm25
5
  openai
 
6
  faiss-cpu
7
  llama-index-embeddings-openai
8
  llama-index-llms-openai
 
3
  llama-index-vector-stores-faiss
4
  llama-index-retrievers-bm25
5
  openai
6
+ anthropic
7
  faiss-cpu
8
  llama-index-embeddings-openai
9
  llama-index-llms-openai