jazzysnake commited on
Commit
f3b1b8f
1 Parent(s): 20c6229

final solution of the 2024 hackathon

Browse files
Files changed (3) hide show
  1. app.py +110 -0
  2. langchain_legal.py +319 -0
  3. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import gradio as gr
4
+ import json
5
+ from langchain_legal import run_pipeline
6
+
7
+ global data
8
+ data = {}
9
+
10
+
11
+ def load_json(path: str) -> dict:
12
+ with open(path, 'r') as f:
13
+ return json.load(f)
14
+
15
+
16
+ def get_ai_final_summary(text_input):
17
+ global data
18
+ data = run_pipeline(text_input)
19
+ return data['ai_final_summary']
20
+
21
+
22
+ STEP_NAMES = [
23
+ 'Initial assessment',
24
+ 'Applicable EU regs',
25
+ 'Applicable national regs',
26
+ 'Complementary assessment',
27
+ ]
28
+
29
+
30
+ def get_inital_assesment() -> str:
31
+ initial = data.get('ai_first_5', None)
32
+ print(data)
33
+ if initial is None:
34
+ return 'No llm answer yet'
35
+ return initial
36
+
37
+
38
+ def get_applicable_eu_regs() -> str:
39
+ eval = data.get('ai_eur_lex_eval', None)
40
+ if eval is None:
41
+ return 'No llm answer yet'
42
+ sources = data.get('eur_lex_lookup')
43
+ res = f'EVALUATION:\n{eval}'
44
+ if sources is not None:
45
+ res = f'SOURCES:\n{sources}\n\n\n' + res
46
+ return res
47
+
48
+
49
+ def get_applicable_national_regs() -> str:
50
+ eval = data.get('ai_aus_eval', None)
51
+ nat = 'aus'
52
+ if eval is None:
53
+ eval = data.get('ai_ger_eval', None)
54
+ nat = 'ger'
55
+ if eval is None:
56
+ return 'No llm answer yet'
57
+ if nat == 'aus':
58
+ sources = data.get('aus_lookup')
59
+ else:
60
+ sources = data.get('ger_lookup')
61
+ res = f'EVALUATION:\n{eval}'
62
+ if sources is not None:
63
+ res = f'SOURCES:\n{sources}\n\n\n' + res
64
+ return res
65
+
66
+
67
+ def get_ai_challange() -> str:
68
+ challange = data.get('ai_challange', None)
69
+ print(data)
70
+ if challange is None:
71
+ return 'No llm answer yet'
72
+ return challange
73
+
74
+
75
+ CALLBACKS = [
76
+ get_inital_assesment,
77
+ get_applicable_eu_regs,
78
+ get_applicable_national_regs,
79
+ get_ai_challange,
80
+ ]
81
+
82
+
83
+ def main():
84
+ with gr.Blocks() as demo:
85
+ gr.Markdown("# Lawgarithm's LLM legal research tool")
86
+
87
+ with gr.Column():
88
+ text_input = gr.Textbox(
89
+ label='Case description',
90
+ placeholder='Write your legal case here',
91
+ )
92
+ submit_btn = gr.Button('Submit')
93
+
94
+ text_output = gr.Textbox(label='Final Summary')
95
+
96
+ submit_btn.click(
97
+ get_ai_final_summary, inputs=text_input, outputs=text_output
98
+ )
99
+
100
+ with gr.Tabs():
101
+ for name, cb in zip(STEP_NAMES, CALLBACKS):
102
+ with gr.Tab(f'{name}') as t:
103
+ tab_text = gr.Textbox(placeholder='No answer yet')
104
+ t.select(cb, outputs=tab_text)
105
+
106
+ demo.launch()
107
+
108
+
109
+ if __name__ == '__main__':
110
+ main()
langchain_legal.py ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from langchain.chains import ConversationChain
4
+ from langchain.schema import SystemMessage, HumanMessage, AIMessage
5
+ import boto3
6
+ from typing import Dict
7
+ from langchain_aws.retrievers import AmazonKnowledgeBasesRetriever
8
+ from langchain_aws import ChatBedrock
9
+
10
+
11
+ def create_bedrock_client(service_type: str = 'bedrock-runtime'):
12
+ """Create and return a Bedrock client with default credentials"""
13
+ return boto3.client(
14
+ service_name=service_type,
15
+ region_name='eu-west-2',
16
+ )
17
+
18
+
19
+ def initialize_claude_chain(client):
20
+ """Initialize and return a conversation chain with Claude"""
21
+ llm = ChatBedrock(
22
+ model='anthropic.claude-3-sonnet-20240229-v1:0',
23
+ client=client,
24
+ model_kwargs={
25
+ 'max_tokens': 1000,
26
+ 'temperature': 0.6,
27
+ 'top_p': 0.9,
28
+ },
29
+ )
30
+ return llm
31
+
32
+
33
+ def chat_with_claude(conversation: ConversationChain, user_input: str) -> Dict:
34
+ """Send a message to Claude and get its response"""
35
+ try:
36
+ response = conversation.predict(input=user_input)
37
+ return {'status': 'success', 'response': response}
38
+ except Exception as e:
39
+ return {'status': 'error', 'error': str(e)}
40
+
41
+
42
+ def classify_kb_response(llm, response: str) -> list[str]:
43
+ kb_classifer = [
44
+ SystemMessage(
45
+ content="""You are a classifier determining the intent of llm responses.
46
+
47
+ The llm can decide to consult 3 databases: ['eur lex', 'austrian law', 'german law']
48
+ Determine the intent and output the relevant classes.
49
+ Examples:
50
+ #1
51
+ <llm indicates eu law intent>
52
+ Output:
53
+ ['eur lex']
54
+ #2
55
+ <llm indicates eu law and austrian law intent>
56
+ Output:
57
+ ['eur lex', 'austrian law']
58
+ #3
59
+ <llm indicates eu law and german law intent>
60
+ Output:
61
+ ['eur lex', 'german law']
62
+ """
63
+ ),
64
+ HumanMessage(content=f'Classify the below context:{response}'),
65
+ ]
66
+ response = llm.invoke(kb_classifer)
67
+ res_str = str(response.content)
68
+ res_parsed = []
69
+ if 'eur lex' in res_str.lower():
70
+ res_parsed.append('eur lex')
71
+ if 'austrian law' in res_str.lower():
72
+ res_parsed.append('austrian law')
73
+ if 'german law' in res_str.lower():
74
+ res_parsed.append('german law')
75
+ return res_parsed
76
+
77
+
78
+ def classify_exception_response(llm, response: str) -> bool:
79
+ kb_classifer = [
80
+ SystemMessage(
81
+ content="""You are a classifier determining the intent of llm responses.
82
+
83
+ The llm can decide if the legal case falls upon an exception, and if so
84
+ can consult a database.
85
+
86
+ Determine the intent and output the relevant classes.
87
+ Examples:
88
+ #1
89
+ <llm indicates no exceptions seem to apply confidently>
90
+ Output:
91
+ 'no exception'
92
+ #2
93
+ <llm indicates there might be some exceptions>
94
+ Output:
95
+ 'database lookup'
96
+ """
97
+ ),
98
+ HumanMessage(content=f'Classify the below context:{response}'),
99
+ ]
100
+ response = llm.invoke(kb_classifer)
101
+ res_str = str(response.content)
102
+ return 'database lookup' in res_str.lower()
103
+
104
+
105
+ def retrieve_knowledge_base_docs(
106
+ client,
107
+ knowledge_base_id: str,
108
+ query: str,
109
+ num_res: int = 25,
110
+ ) -> list:
111
+ retriever = AmazonKnowledgeBasesRetriever(
112
+ client=client,
113
+ knowledge_base_id=knowledge_base_id,
114
+ retrieval_config={
115
+ 'vectorSearchConfiguration': {'numberOfResults': num_res}
116
+ },
117
+ min_score_confidence=0.0,
118
+ )
119
+ return retriever.invoke(query)
120
+
121
+
122
+ case1 = """A new member is about to be appointed to the management body of a(n ECB) supervised institution in Austria. The
123
+ prospective member is already a member of the board in three other group companies. The supervisor is concerned the potential board
124
+ member does not have enough time to commit to this new position. Can the ECB as supervisor oppose this appointment?
125
+ """
126
+
127
+ case2 = """A shareholder in a German bank did not ask approval to the ECB for the
128
+ acquisition of its qualifying holding (10% or more) in the German bank.
129
+ What can be the consequences?
130
+ """
131
+
132
+ case3 = """A request for public access to ECB documents based on Decision ECB/2004/3
133
+ (2004/258/EC) aims at getting access to documents containing the proceedings
134
+ and the outcome of the deliberations of a Governing Council meeting.
135
+ Can the documents be disclosed under the applicable legal framework?
136
+ """
137
+
138
+ case4 = """In relation to the Corporate sector purchase programme (CSPP):
139
+ Is commercial paper an eligible asset under the CSPP? Under what
140
+ conditions?
141
+ """
142
+
143
+
144
+ SYSTEM = 'You are a lawyer, giving professional and accurate legal advice. Base your answers on relevant, up todate legal frameworks.'
145
+
146
+
147
+ def run_pipeline(case_text: str | None = None):
148
+ client = create_bedrock_client()
149
+ retrieval_client = create_bedrock_client('bedrock-agent-runtime')
150
+ chat = initialize_claude_chain(client)
151
+ export = {}
152
+ case = ''
153
+ if case_text is None:
154
+ case = case1
155
+ else:
156
+ case = case_text
157
+
158
+ human_input = f"""
159
+ <case>{case}</case>
160
+
161
+ FOLLOW THE STEP BY STEP PLAN:
162
+ 1. Summarize the question
163
+ 2. Identify the main actors and their relationships (if applicable)
164
+ 3. Identify who is competent on the legal matter and reference the legal basis
165
+ 4. Identify the legal issue based on the inital question.
166
+ 5. Which knowledge bases are most likely to contain relevant information? (Eur lex, austrian law, german law)
167
+ """
168
+ export['human_1'] = human_input
169
+ main_wf_messages = [
170
+ SystemMessage(
171
+ content=SYSTEM,
172
+ ),
173
+ HumanMessage(
174
+ content=human_input,
175
+ ),
176
+ ]
177
+ AUS_KB_ID = 'URGID9GFK8'
178
+ GER_KB_ID = '19H00QAPZG'
179
+ EU_KB_ID = 'ORMULTAIWL'
180
+
181
+ response = chat.invoke(main_wf_messages)
182
+ fifth_point = str(response.content).split('5. ')[-1]
183
+ print(response.content)
184
+ export['ai_first_5'] = str(response.content)
185
+ main_wf_messages.append(
186
+ AIMessage(content=str(response.content)),
187
+ )
188
+ print('------')
189
+ print(fifth_point)
190
+ print('------')
191
+ classification = classify_kb_response(chat, fifth_point)
192
+ export['ai_lookup_decision_1'] = classification
193
+ print('CLASS', classification)
194
+ if 'eur lex' in classification:
195
+ eu_res = retrieve_knowledge_base_docs(
196
+ retrieval_client, EU_KB_ID, str(response.content)
197
+ )
198
+ export['eur_lex_lookup'] = [str(r) for r in eu_res]
199
+ main_wf_messages.append(
200
+ HumanMessage(
201
+ content=f'Determine which regulations apply (from eur lex):\n{eu_res}'
202
+ )
203
+ )
204
+ response = chat.invoke(main_wf_messages)
205
+ print(response.content)
206
+ export['ai_eur_lex_eval'] = str(response.content)
207
+ main_wf_messages.append(
208
+ AIMessage(content=str(response.content)),
209
+ )
210
+ if 'austrian law' in classification:
211
+ aus_res = retrieve_knowledge_base_docs(
212
+ retrieval_client,
213
+ AUS_KB_ID,
214
+ f'Determine which regulations apply (from austrian law):\n{case}',
215
+ )
216
+ export['aus_lookup'] = [str(r) for r in aus_res]
217
+ main_wf_messages.append(
218
+ HumanMessage(
219
+ content=f'Determine which regulations apply (from austrian law):\n{aus_res}'
220
+ )
221
+ )
222
+ response = chat.invoke(main_wf_messages)
223
+ print(response.content)
224
+ export['ai_aus_eval'] = str(response.content)
225
+ main_wf_messages.append(
226
+ AIMessage(content=str(response.content)),
227
+ )
228
+ if 'german law' in classification:
229
+ ger_res = retrieve_knowledge_base_docs(
230
+ retrieval_client,
231
+ GER_KB_ID,
232
+ f'Determine which regulations apply (from german law):\n{human_input}',
233
+ )
234
+ export['ger_lookup'] = [str(r) for r in ger_res]
235
+ main_wf_messages.append(
236
+ HumanMessage(
237
+ content=f'Determine which regulations apply (from german law):\n{ger_res}'
238
+ )
239
+ )
240
+ response = chat.invoke(main_wf_messages)
241
+ print(response.content)
242
+ export['ai_ger_eval'] = str(response.content)
243
+ main_wf_messages.append(
244
+ AIMessage(content=str(response.content)),
245
+ )
246
+ # EXCEPTION_PROMPT = """6. Determine if the case falls upon an exception and
247
+ # retrieve the relevant regulation connected to the exception"""
248
+ # export['human_exception'] = EXCEPTION_PROMPT
249
+ # main_wf_messages.append(
250
+ # HumanMessage(
251
+ # content=EXCEPTION_PROMPT,
252
+ # )
253
+ # )
254
+ # response = chat.invoke(main_wf_messages)
255
+ # print(response.content)
256
+ # export['ai_exception_investigation'] = str(response.content)
257
+ # main_wf_messages.append(
258
+ # AIMessage(content=str(response.content)),
259
+ # )
260
+ # is_exception = classify_exception_response(chat, str(response.content))
261
+ # print('IS EXCEPTION CLASS:', is_exception)
262
+ # if is_exception:
263
+ # eu_res = retrieve_knowledge_base_docs(
264
+ # retrieval_client,
265
+ # EU_KB_ID,
266
+ # str(response.content),
267
+ # 10,
268
+ # )
269
+ # export['ai_exception_lookup'] = [str(r) for r in eu_res]
270
+ # main_wf_messages.append(
271
+ # HumanMessage(
272
+ # content=f'Determine if the regulations provide a strong legal basis for an exception:\n{eu_res}'
273
+ # )
274
+ # )
275
+ # response = chat.invoke(main_wf_messages)
276
+ # print(response.content)
277
+ # export['ai_exception_eval'] = str(response.content)
278
+ # main_wf_messages.append(
279
+ # AIMessage(content=str(response.content)),
280
+ # )
281
+ #
282
+ FINAL_SUMMARY_PROMPT = """6.Based on the actors, the competencies, the relevant legal frameworks
283
+ as mentioned above draft an answer to the FULL original question(s).
284
+ Mention the legal issue(s), the relevant articles used in both eur lex and national and the
285
+ applicable regime set up, the conditions if applicable, conclusion."""
286
+ export['human_summary'] = FINAL_SUMMARY_PROMPT
287
+ main_wf_messages.append(
288
+ HumanMessage(
289
+ content=FINAL_SUMMARY_PROMPT,
290
+ )
291
+ )
292
+ response = chat.invoke(main_wf_messages)
293
+ print('----------FINAL RESPONSE------------')
294
+ print(response.content)
295
+ export['ai_final_summary'] = str(response.content)
296
+ main_wf_messages.append(
297
+ AIMessage(content=str(response.content)),
298
+ )
299
+
300
+ main_wf_messages.append(
301
+ HumanMessage(
302
+ content="""You are a lawyer and you need explain your answer to the question(s).
303
+ Your colleagues are saying you are wrong on at least one of these aspects: competencies, relevant laws, articles references (missing or hallucination).
304
+ Draft a complete explanation of your reasoning, mention the legal issue(s), the correct complete relevant articles used in both eur lex and national and the applicable regime set up, the conditions if applicable, conclusions."""
305
+ )
306
+ )
307
+
308
+ response = chat.invoke(main_wf_messages)
309
+ print('----------Complementary------------')
310
+ print(response.content)
311
+ export['ai_challange'] = str(response.content)
312
+ return export
313
+
314
+
315
+ if __name__ == '__main__':
316
+ export = run_pipeline()
317
+ print(json.dumps(export, indent=4))
318
+ with open('export.json', 'w') as f:
319
+ json.dump(export, f, indent=4)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ langchain
2
+ langchain-community
3
+ langchain-aws
4
+ boto3
5
+ gradio