DocUA commited on
Commit
4d466aa
·
1 Parent(s): 4d2272d

refactoring

Browse files
Files changed (3) hide show
  1. analysis.py +203 -0
  2. config.py +29 -0
  3. main.py +19 -212
analysis.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from llama_index.core.workflow import Workflow, Context, StartEvent, StopEvent, step
2
+ import json
3
+ from prompts import PRECEDENT_ANALYSIS_TEMPLATE
4
+ from enum import Enum
5
+ from anthropic import Anthropic
6
+ from llama_index.llms.openai import OpenAI
7
+ from llama_index.core.llms import ChatMessage
8
+ from config import embed_model, Settings, openai_api_key, anthropic_api_key
9
+
10
+
11
+ class ModelProvider(str, Enum):
12
+ OPENAI = "openai"
13
+ ANTHROPIC = "anthropic"
14
+
15
+
16
+ class ModelName(str, Enum):
17
+ # OpenAI models
18
+ GPT4o = "gpt-4o"
19
+ GPT4o_MINI = "gpt-4o-mini"
20
+ # Anthropic models
21
+ CLAUDE3_5_SONNET = "claude-3-5-sonnet-latest"
22
+ CLAUDE3_5_HAIKU = "claude-3-5-haiku-latest"
23
+
24
+
25
+ class LLMAnalyzer:
26
+ def __init__(self, provider: ModelProvider, model_name: ModelName):
27
+ self.provider = provider
28
+ self.model_name = model_name
29
+
30
+ if provider == ModelProvider.OPENAI:
31
+ self.client = OpenAI(model=model_name)
32
+ elif provider == ModelProvider.ANTHROPIC:
33
+ # Додаємо API ключ при ініціалізації
34
+ self.client = Anthropic(api_key=anthropic_api_key)
35
+ else:
36
+ raise ValueError(f"Unsupported provider: {provider}")
37
+
38
+ async def analyze(self, prompt: str, response_schema: dict) -> str:
39
+ if self.provider == ModelProvider.OPENAI:
40
+ return await self._analyze_with_openai(prompt, response_schema)
41
+ else:
42
+ return await self._analyze_with_anthropic(prompt, response_schema)
43
+
44
+ async def _analyze_with_openai(self, prompt: str, response_schema: dict) -> str:
45
+ messages = [
46
+ ChatMessage(role="system",
47
+ content="Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду."),
48
+ ChatMessage(role="user", content=prompt)
49
+ ]
50
+
51
+ # Правильний формат для response_format
52
+ response_format = {
53
+ "type": "json_schema",
54
+ "json_schema": {
55
+ "name": "relevant_positions_schema", # Додаємо обов'язкове поле name
56
+ "schema": response_schema
57
+ }
58
+ }
59
+
60
+ response = self.client.chat(
61
+ messages=messages,
62
+ response_format=response_format,
63
+ temperature=0,
64
+ max_tokens=4096
65
+ )
66
+ return response.message.content
67
+
68
+ async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
69
+ response = self.client.messages.create( # Прибрали await
70
+ model=self.model_name,
71
+ max_tokens=4096,
72
+ messages=[
73
+ {
74
+ "role": "assistant",
75
+ "content": "Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду."
76
+ },
77
+ {
78
+ "role": "user",
79
+ "content": prompt
80
+ }
81
+ ]
82
+ )
83
+ return response.content[0].text
84
+
85
+
86
+ class PrecedentAnalysisWorkflow(Workflow):
87
+ def __init__(self, provider: ModelProvider = ModelProvider.OPENAI,
88
+ model_name: ModelName = ModelName.GPT4o_MINI):
89
+ super().__init__()
90
+ self.analyzer = LLMAnalyzer(provider, model_name)
91
+
92
+ @step
93
+ async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
94
+ try:
95
+ # Отримуємо параметри з події з дефолтними значеннями
96
+ query = ev.get("query", "")
97
+ question = ev.get("question", "")
98
+ nodes = ev.get("nodes", [])
99
+
100
+ # Перевірка на пусті значення
101
+ if not query:
102
+ return StopEvent(result="Помилка: Не надано текст нового рішення (query)")
103
+
104
+ if not nodes:
105
+ return StopEvent(result="Помилка: Не надано правові позиції для аналізу (nodes)")
106
+
107
+ # Підготовка контексту
108
+ context_parts = []
109
+ for i, node in enumerate(nodes, 1):
110
+ node_text = node.node.text if hasattr(node, 'node') else node.text
111
+ metadata = node.node.metadata if hasattr(node, 'node') else node.metadata
112
+ lp_id = metadata.get('lp_id', f'unknown_{i}')
113
+ context_parts.append(f"Source {i} (ID: {lp_id}):\n{node_text}")
114
+
115
+ context_str = "\n\n".join(context_parts)
116
+
117
+ # Схема відповіді
118
+ response_schema = {
119
+ "type": "object",
120
+ "properties": {
121
+ "relevant_positions": {
122
+ "type": "array",
123
+ "items": {
124
+ "type": "object",
125
+ "properties": {
126
+ "lp_id": {"type": "string"},
127
+ "source_index": {"type": "string"},
128
+ "description": {"type": "string"}
129
+ },
130
+ "required": ["lp_id", "source_index", "description"]
131
+ }
132
+ }
133
+ },
134
+ "required": ["relevant_positions"]
135
+ }
136
+
137
+ # Формування промпту
138
+ prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
139
+ query=query,
140
+ question=question if question else "Загальний аналіз релевантності",
141
+ context_str=context_str
142
+ )
143
+
144
+ # Отримання відповіді від моделі
145
+ response_content = await self.analyzer.analyze(prompt, response_schema)
146
+
147
+ try:
148
+ parsed_response = json.loads(response_content)
149
+ if "relevant_positions" in parsed_response:
150
+ response_lines = []
151
+ for position in parsed_response["relevant_positions"]:
152
+ position_text = (
153
+ f"* [{position['source_index']}] {position['description']} "
154
+ )
155
+ response_lines.append(position_text)
156
+
157
+ response_text = "\n".join(response_lines)
158
+ return StopEvent(result=response_text)
159
+ else:
160
+ return StopEvent(result="Не знайдено релевантних правових позицій")
161
+
162
+ except json.JSONDecodeError:
163
+ return StopEvent(result="Помилка обробки відповіді від AI")
164
+
165
+ except Exception as e:
166
+ return StopEvent(result=f"Error during analysis: {str(e)}")
167
+
168
+ # Формування промпту та отримання відповіді
169
+ prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
170
+ query=query,
171
+ question=question if question else "Загальний аналіз релевантності",
172
+ context_str=context_str
173
+ )
174
+
175
+ messages = [
176
+ ChatMessage(role="system", content="Ти - кваліфікований юрист-аналітик."),
177
+ ChatMessage(role="user", content=prompt)
178
+ ]
179
+
180
+ response = llm_analyse.chat(
181
+ messages=messages,
182
+ response_format=response_format
183
+ )
184
+
185
+ try:
186
+ parsed_response = json.loads(response.message.content)
187
+ if "relevant_positions" in parsed_response:
188
+ # Форматуємо результат
189
+ response_lines = []
190
+
191
+ for position in parsed_response["relevant_positions"]:
192
+ position_text = (
193
+ f"* [{position['source_index']}]: {position['description']} "
194
+ )
195
+ response_lines.append(position_text)
196
+
197
+ response_text = "\n".join(response_lines)
198
+ return StopEvent(result=response_text)
199
+ else:
200
+ return StopEvent(result="Помилка: відповідь не містить аналізу правових позицій")
201
+
202
+ except json.JSONDecodeError:
203
+ return StopEvent(result="Помилка обробки відповіді від AI")
config.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import google.generativeai as genai
3
+ from llama_index.embeddings.openai import OpenAIEmbedding
4
+
5
+
6
+ from llama_index.core import (
7
+ StorageContext,
8
+ ServiceContext,
9
+ VectorStoreIndex,
10
+ Settings,
11
+ load_index_from_storage
12
+ )
13
+
14
+ from dotenv import load_dotenv
15
+
16
+ load_dotenv()
17
+
18
+ aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID")
19
+ aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY")
20
+ openai_api_key = os.getenv("OPENAI_API_KEY")
21
+ anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")
22
+ genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
23
+
24
+
25
+ embed_model = OpenAIEmbedding(model_name="text-embedding-3-small")
26
+ Settings.embed_model = embed_model
27
+ Settings.context_window = 20000
28
+ Settings.chunk_size = 2048
29
+ Settings.similarity_top_k = 20
main.py CHANGED
@@ -36,25 +36,28 @@ from llama_index.core.schema import NodeWithScore
36
  from llama_index.core.prompts import PromptTemplate
37
  from llama_index.core.response_synthesizers import ResponseMode, get_response_synthesizer
38
 
39
- from prompts import SYSTEM_PROMPT, LEGAL_POSITION_PROMPT, PRECEDENT_ANALYSIS_TEMPLATE
40
-
41
-
42
- from dotenv import load_dotenv
43
 
44
- load_dotenv()
45
-
46
- aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID")
47
- aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY")
48
- openai_api_key = os.getenv("OPENAI_API_KEY")
49
- anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")
50
- genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
51
 
52
 
53
- embed_model = OpenAIEmbedding(model_name="text-embedding-3-small")
54
- Settings.embed_model = embed_model
55
- Settings.context_window = 20000
56
- Settings.chunk_size = 2048
57
- Settings.similarity_top_k = 20
 
 
 
 
 
 
 
 
 
 
 
58
 
59
 
60
  # Параметри S3
@@ -117,202 +120,6 @@ state_lp_json = gr.State()
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
- GPT4o = "gpt-4o"
131
- GPT4o_MINI = "gpt-4o-mini"
132
- # Anthropic models
133
- CLAUDE3_5_SONNET = "claude-3-5-sonnet-latest"
134
- CLAUDE3_5_HAIKU = "claude-3-5-haiku-latest"
135
-
136
-
137
- class LLMAnalyzer:
138
- def __init__(self, provider: ModelProvider, model_name: ModelName):
139
- self.provider = provider
140
- self.model_name = model_name
141
-
142
- if provider == ModelProvider.OPENAI:
143
- self.client = OpenAI(model=model_name)
144
- elif provider == ModelProvider.ANTHROPIC:
145
- # Додаємо API ключ при ініціалізації
146
- self.client = Anthropic(api_key=anthropic_api_key)
147
- else:
148
- raise ValueError(f"Unsupported provider: {provider}")
149
-
150
- async def analyze(self, prompt: str, response_schema: dict) -> str:
151
- if self.provider == ModelProvider.OPENAI:
152
- return await self._analyze_with_openai(prompt, response_schema)
153
- else:
154
- return await self._analyze_with_anthropic(prompt, response_schema)
155
-
156
- async def _analyze_with_openai(self, prompt: str, response_schema: dict) -> str:
157
- messages = [
158
- ChatMessage(role="system",
159
- content="Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду."),
160
- ChatMessage(role="user", content=prompt)
161
- ]
162
-
163
- # Правильний формат для response_format
164
- response_format = {
165
- "type": "json_schema",
166
- "json_schema": {
167
- "name": "relevant_positions_schema", # Додаємо обов'язкове поле name
168
- "schema": response_schema
169
- }
170
- }
171
-
172
- response = self.client.chat(
173
- messages=messages,
174
- response_format=response_format,
175
- temperature=0
176
- )
177
- return response.message.content
178
-
179
- async def _analyze_with_anthropic(self, prompt: str, response_schema: dict) -> str:
180
- response = self.client.messages.create( # Прибрали await
181
- model=self.model_name,
182
- max_tokens=2000,
183
- messages=[
184
- {
185
- "role": "assistant",
186
- "content": "Ти - кваліфікований юрист-аналітик, експерт з правових позицій Верховного Суду."
187
- },
188
- {
189
- "role": "user",
190
- "content": prompt
191
- }
192
- ]
193
- )
194
- return response.content[0].text
195
-
196
-
197
- class PrecedentAnalysisWorkflow(Workflow):
198
- def __init__(self, provider: ModelProvider = ModelProvider.OPENAI,
199
- model_name: ModelName = ModelName.GPT4o_MINI):
200
- super().__init__()
201
- self.analyzer = LLMAnalyzer(provider, model_name)
202
-
203
- @step
204
- async def analyze(self, ctx: Context, ev: StartEvent) -> StopEvent:
205
- try:
206
- # Отримуємо параметри з події з дефолтними значеннями
207
- query = ev.get("query", "")
208
- question = ev.get("question", "")
209
- nodes = ev.get("nodes", [])
210
-
211
- # Перевірка на пусті значення
212
- if not query:
213
- return StopEvent(result="Помилка: Не надано текст нового рішення (query)")
214
-
215
- if not nodes:
216
- return StopEvent(result="Помилка: Не надано правові позиції для аналізу (nodes)")
217
-
218
- # Підготовка контексту
219
- context_parts = []
220
- for i, node in enumerate(nodes, 1):
221
- node_text = node.node.text if hasattr(node, 'node') else node.text
222
- metadata = node.node.metadata if hasattr(node, 'node') else node.metadata
223
- lp_id = metadata.get('lp_id', f'unknown_{i}')
224
- context_parts.append(f"Source {i} (ID: {lp_id}):\n{node_text}")
225
-
226
- context_str = "\n\n".join(context_parts)
227
-
228
- # Схема відповіді
229
- response_schema = {
230
- "type": "object",
231
- "properties": {
232
- "relevant_positions": {
233
- "type": "array",
234
- "items": {
235
- "type": "object",
236
- "properties": {
237
- "lp_id": {"type": "string"},
238
- "source_index": {"type": "string"},
239
- "description": {"type": "string"}
240
- },
241
- "required": ["lp_id", "source_index", "description"]
242
- }
243
- }
244
- },
245
- "required": ["relevant_positions"]
246
- }
247
-
248
- # Формування промпту
249
- prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
250
- query=query,
251
- question=question if question else "Загальний аналіз релевантності",
252
- context_str=context_str
253
- )
254
-
255
- # Отримання відповіді від моделі
256
- response_content = await self.analyzer.analyze(prompt, response_schema)
257
-
258
- try:
259
- parsed_response = json.loads(response_content)
260
- if "relevant_positions" in parsed_response:
261
- response_lines = []
262
- for position in parsed_response["relevant_positions"]:
263
- position_text = (
264
- f"* [{position['source_index']}] {position['description']} "
265
- )
266
- response_lines.append(position_text)
267
-
268
- response_text = "\n".join(response_lines)
269
- return StopEvent(result=response_text)
270
- else:
271
- return StopEvent(result="Не знайдено релевантних правових позицій")
272
-
273
- except json.JSONDecodeError:
274
- return StopEvent(result="Помилка обробки відповіді від AI")
275
-
276
- except Exception as e:
277
- return StopEvent(result=f"Error during analysis: {str(e)}")
278
-
279
- # Формування промпту та отримання відповіді
280
- prompt = PRECEDENT_ANALYSIS_TEMPLATE.format(
281
- query=query,
282
- question=question if question else "Загальний аналіз релевантності",
283
- context_str=context_str
284
- )
285
-
286
- messages = [
287
- ChatMessage(role="system", content="Ти - кваліфікований юрист-аналітик."),
288
- ChatMessage(role="user", content=prompt)
289
- ]
290
-
291
- response = llm_analyse.chat(
292
- messages=messages,
293
- response_format=response_format
294
- )
295
-
296
- try:
297
- parsed_response = json.loads(response.message.content)
298
- if "relevant_positions" in parsed_response:
299
- # Форматуємо результат
300
- response_lines = []
301
-
302
- for position in parsed_response["relevant_positions"]:
303
- position_text = (
304
- f"* [{position['source_index']}]: {position['description']} "
305
- )
306
- response_lines.append(position_text)
307
-
308
- response_text = "\n".join(response_lines)
309
- return StopEvent(result=response_text)
310
- else:
311
- return StopEvent(result="Помилка: відповідь не містить аналізу правових позицій")
312
-
313
- except json.JSONDecodeError:
314
- return StopEvent(result="Помилка обробки відповіді від AI")
315
-
316
 
317
  def parse_doc_ids(doc_ids):
318
  if doc_ids is None:
 
36
  from llama_index.core.prompts import PromptTemplate
37
  from llama_index.core.response_synthesizers import ResponseMode, get_response_synthesizer
38
 
39
+ from config import embed_model, Settings, openai_api_key, anthropic_api_key, aws_access_key_id, aws_secret_access_key
40
+ from analysis import ModelProvider, ModelName, PrecedentAnalysisWorkflow
 
 
41
 
42
+ from prompts import SYSTEM_PROMPT, LEGAL_POSITION_PROMPT, PRECEDENT_ANALYSIS_TEMPLATE
 
 
 
 
 
 
43
 
44
 
45
+ # from dotenv import load_dotenv
46
+ #
47
+ # load_dotenv()
48
+ #
49
+ # aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID")
50
+ # aws_secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY")
51
+ # openai_api_key = os.getenv("OPENAI_API_KEY")
52
+ # anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")
53
+ # genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
54
+ #
55
+ #
56
+ # embed_model = OpenAIEmbedding(model_name="text-embedding-3-small")
57
+ # Settings.embed_model = embed_model
58
+ # Settings.context_window = 20000
59
+ # Settings.chunk_size = 2048
60
+ # Settings.similarity_top_k = 20
61
 
62
 
63
  # Параметри S3
 
120
  state_nodes = gr.State()
121
 
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  def parse_doc_ids(doc_ids):
125
  if doc_ids is None: