1- import os
21import json
32import re
43
87from rest_framework import status
98from django .utils .decorators import method_decorator
109from django .views .decorators .csrf import csrf_exempt
11- import anthropic
1210
1311from ...services .openai_services import openAIServices
1412from api .models .model_embeddings import Embeddings
1513
16- USER_PROMPT = """
17- I'm creating a system to analyze medical research. It processes peer-reviewed papers to extract key details
18-
19- Act as a seasoned physician or medical professional who treat patients with bipolar disorder
20-
21- Identify rules for medication inclusion or exclusion based on medical history or concerns
22-
23- Return an output with the same structure as these examples:
24-
25- The rule is history of suicide attempts. The type of rule is "INCLUDE". The reason is lithium is the
26- only medication on the market that has been proven to reduce suicidality in patients with bipolar disorder.
27- The medications for this rule are lithium.
28-
29- The rule is weight gain concerns. The type of rule is "EXCLUDE". The reason is Seroquel, Risperdal, Abilify, and
30- Zyprexa are known for causing weight gain. The medications for this rule are Quetiapine, Aripiprazole, Olanzapine, Risperidone
31- }
32- """
33-
34-
35- def anthropic_citations (client : anthropic .Client , user_prompt : str , content_chunks : list ) -> tuple :
36- """
37- Sends a message to Anthropic Citations and extract and format the response
38-
39- Parameters
40- ----------
41- client: An instance of the Anthropic API client used to make the request
42- user_prompt: The user's question or instruction to be processed by the model
43- content_chunks: A list of text chunks that provide context for the model to use during generation
44-
45- Returns
46- -------
47- tuple
48-
49- """
50-
51-
52- message = client .messages .create (
53- model = "claude-3-5-haiku-20241022" ,
54- max_tokens = 1024 ,
55- messages = [
56- {
57- "role" : "user" ,
58- "content" : [
59- {
60- "type" : "document" ,
61- "source" : {
62- "type" : "content" ,
63- "content" : content_chunks
64- },
65- "citations" : {"enabled" : True }
66- },
67-
68- {
69- "type" : "text" ,
70- "text" : user_prompt
71- }
72- ]
73- }
74- ],
75- )
76-
77- # Response Structure: https://docs.anthropic.com/en/docs/build-with-claude/citations#response-structure
78-
79- text = []
80- cited_text = []
81- for content in message .to_dict ()['content' ]:
82- text .append (content ['text' ])
83- if 'citations' in content .keys ():
84- text .append (" " .join (
85- [f"<{ citation ['start_block_index' ]} - { citation ['end_block_index' ]} >" for citation in content ['citations' ]]))
86- cited_text .append (" " .join (
87- [f"<{ citation ['start_block_index' ]} - { citation ['end_block_index' ]} > { citation ['cited_text' ]} " for citation in content ['citations' ]]))
88-
89- texts = " " .join (text )
90- cited_texts = " " .join (cited_text )
91-
92- return texts , cited_texts
93-
94-
95- @method_decorator (csrf_exempt , name = 'dispatch' )
96- class RuleExtractionAPIView (APIView ):
97-
98- permission_classes = [IsAuthenticated ]
99-
100- def get (self , request ):
101- try :
102-
103- client = anthropic .Anthropic (api_key = os .getenv ("ANTHROPIC_API_KEY" ))
104-
105- guid = request .query_params .get ('guid' )
106-
107- query = Embeddings .objects .filter (upload_file__guid = guid )
108-
109- # TODO: Format into the Anthropic API"s expected input format in the anthropic_citations function
110- chunks = [{"type" : "text" , "text" : chunk .text } for chunk in query ]
111-
112- texts , cited_texts = anthropic_citations (client , USER_PROMPT , chunks )
113-
114-
115- return Response ({"texts" : texts , "cited_texts" : cited_texts }, status = status .HTTP_200_OK )
116-
117- except Exception as e :
118- return Response ({"error" : str (e )}, status = status .HTTP_500_INTERNAL_SERVER_ERROR )
119-
120-
12114# This is to use openai to extract the rules to save cost
12215
16+
12317def openai_extraction (content_chunks , user_prompt ):
12418 """
12519 Prepares the OpenAI input and returns the extracted text.
12620 """
12721
128- combined_text = "\n \n " .join (chunk [' text' ] for chunk in content_chunks )
22+ combined_text = "\n \n " .join (chunk [" text" ] for chunk in content_chunks )
12923
13024 result = openAIServices .openAI (
13125 userMessage = combined_text ,
13226 prompt = user_prompt ,
13327 model = "gpt-4o-mini" ,
13428 temp = 0.0 ,
135- stream = False
29+ stream = False ,
13630 )
13731 return result
13832
13933
140- @method_decorator (csrf_exempt , name = ' dispatch' )
34+ @method_decorator (csrf_exempt , name = " dispatch" )
14135class RuleExtractionAPIOpenAIView (APIView ):
14236 permission_classes = [IsAuthenticated ]
14337
@@ -167,21 +61,19 @@ def get(self, request):
16761 Return the entire output as a JSON array.
16862 """
16963
170- guid = request .query_params .get (' guid' )
64+ guid = request .query_params .get (" guid" )
17165 query = Embeddings .objects .filter (upload_file__guid = guid )
17266 chunks = [
17367 {"type" : "text" , "text" : f"[chunk-{ i } ] { chunk .text } " }
17468 for i , chunk in enumerate (query )
17569 ]
17670
17771 output_text = openai_extraction (chunks , user_prompt )
178- cleaned_text = re .sub (r"^```json|```$" , "" ,
179- output_text .strip ()).strip ()
72+ cleaned_text = re .sub (r"^```json|```$" , "" , output_text .strip ()).strip ()
18073 rules = json .loads (cleaned_text )
18174
18275 # Attach chunk_number and chunk_text to each rule
183- chunk_lookup = {f"chunk-{ i } " : chunk .text for i ,
184- chunk in enumerate (query )}
76+ chunk_lookup = {f"chunk-{ i } " : chunk .text for i , chunk in enumerate (query )}
18577 for rule in rules :
18678 source = rule .get ("source" , "" ).strip ("[]" ) # e.g. chunk-63
18779 if source .startswith ("chunk-" ):
@@ -192,4 +84,6 @@ def get(self, request):
19284 return Response ({"rules" : rules }, status = status .HTTP_200_OK )
19385
19486 except Exception as e :
195- return Response ({"error" : str (e )}, status = status .HTTP_500_INTERNAL_SERVER_ERROR )
87+ return Response (
88+ {"error" : str (e )}, status = status .HTTP_500_INTERNAL_SERVER_ERROR
89+ )
0 commit comments